Merge remote-tracking branch 'upstream/master'

Conflicts:
	firmware/Makefile
	firmware/application/Makefile
	firmware/application/event_m0.cpp
	firmware/application/ui_setup.cpp
	firmware/application/ui_setup.hpp
	firmware/baseband/baseband_thread.cpp
	firmware/baseband/baseband_thread.hpp
	firmware/bootstrap/CMakeLists.txt
	firmware/common/message.hpp
	firmware/common/portapack_shared_memory.hpp
	hardware/.gitignore
This commit is contained in:
furrtek 2016-07-25 16:35:42 +02:00
commit fdfa7c9776
138 changed files with 17603 additions and 1930 deletions

View File

@ -11,27 +11,33 @@ notifications:
- "Change view : %{compare_url}"
- "Build details : %{build_url}"
# TODO: The "build_number.1" in this URL is almost certainly wrong, but correct value not available from Travis?
- "Firmware download : https://portapack-h1-builds.s3.amazonaws.com/%{repository_slug}/%{build_number}/%{build_number}.1/firmware/portapack-h1-firmware-%{commit}.tar.bz2"
- "Firmware download : https://portapack-h1-builds.s3.amazonaws.com/%{repository_slug}/%{build_number}/%{build_number}.1/build/firmware/portapack-h1-firmware-%{commit}.tar.bz2"
before_script:
- wget https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q1-update/+download/gcc-arm-none-eabi-5_3-2016q1-20160330-linux.tar.bz2 -O /tmp/gcc-arm.tar.bz2
- wget https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q2-update/+download/gcc-arm-none-eabi-5_4-2016q2-20160622-linux.tar.bz2 -O /tmp/gcc-arm.tar.bz2
- tar -xf /tmp/gcc-arm.tar.bz2
- export PATH=$PWD/gcc-arm-none-eabi-5_3-2016q1/bin:$PATH
- export PATH=$PWD/gcc-arm-none-eabi-5_4-2016q2/bin:$PATH
- export CC="arm-none-eabi-gcc"
- export CXX="arm-none-eabi-g++"
script:
# TODO: Introduce top-level Makefile, this is lame.
- pushd firmware/
- mkdir build/
- pushd build/
- cmake ..
- make release
- popd
addons:
apt_packages:
- lib32bz2-1.0
- lib32ncurses5
- lib32z1
apt:
packages:
- lib32bz2-1.0
- lib32ncurses5
- lib32z1
- cmake
sources:
- kalakris-cmake
artifacts:
paths:
- $(ls firmware/portapack-h1-firmware-*.tar.bz2 | tr "\n" ":")
- $(ls firmware/portapack-h1-firmware-*.zip | tr "\n" ":")
- $(ls build/firmware/portapack-h1-firmware-*.tar.bz2 | tr "\n" ":")
- $(ls build/firmware/portapack-h1-firmware-*.zip | tr "\n" ":")

43
CMakeLists.txt Normal file
View File

@ -0,0 +1,43 @@
# Copyright 2016 Jared Boone <jared@sharebrained.com>
#
# This file is part of PortaPack.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
cmake_minimum_required(VERSION 2.8.9)
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex-m.cmake)
project(portapack-h1)
execute_process(
COMMAND git log -n 1 --format=%h
WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
RESULT_VARIABLE GIT_REVISION_FOUND
ERROR_QUIET
OUTPUT_VARIABLE GIT_REVISION
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (GIT_REVISION_FOUND)
set(VERSION "unknown")
else (GIT_REVISION_FOUND)
set(VERSION ${GIT_REVISION})
endif (GIT_REVISION_FOUND)
set(LICENSE_PATH ${CMAKE_CURRENT_LIST_DIR}/LICENSE)
set(HARDWARE_PATH ${CMAKE_CURRENT_LIST_DIR}/hardware)
add_subdirectory(firmware)

83
firmware/CMakeLists.txt Normal file
View File

@ -0,0 +1,83 @@
# Copyright 2016 Jared Boone <jared@sharebrained.com>
#
# This file is part of PortaPack.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
project(firmware)
set(COMMON ${PROJECT_SOURCE_DIR}/common)
set(CHIBIOS ${PROJECT_SOURCE_DIR}/chibios)
set(CHIBIOS_PORTAPACK ${PROJECT_SOURCE_DIR}/chibios-portapack)
set(HACKRF_FIRMWARE_FILENAME hackrf_one_usb_ram.dfu)
set(HACKRF_FIRMWARE_IMAGE ${PROJECT_SOURCE_DIR}/${HACKRF_FIRMWARE_FILENAME})
set(HACKRF_CPLD_SVF_FILENAME hackrf_cpld_default.svf)
set(HACKRF_CPLD_SVF_PATH ${PROJECT_SOURCE_DIR}/${HACKRF_CPLD_SVF_FILENAME})
set(EXTRACT_CPLD_DATA ${PROJECT_SOURCE_DIR}/tools/extract_cpld_data.py)
set(EXTRACT_SVF_DATA_XC2C64A ${PROJECT_SOURCE_DIR}/tools/extract_svf_data_xc2c64a.py)
set(STRIP_DFU ${PROJECT_SOURCE_DIR}/tools/strip_dfu.py)
set(MAKE_SPI_IMAGE ${PROJECT_SOURCE_DIR}/tools/make_spi_image.py)
set(MAKE_IMAGE_CHUNK ${PROJECT_SOURCE_DIR}/tools/make_image_chunk.py)
set(FIRMWARE_NAME portapack-h1-firmware)
set(FIRMWARE_FILENAME ${FIRMWARE_NAME}.bin)
add_subdirectory(application)
add_subdirectory(baseband)
add_subdirectory(bootstrap)
# NOTE: Dependencies break if the .bin files aren't included in DEPENDS. WTF, CMake?
add_custom_command(
OUTPUT ${FIRMWARE_FILENAME}
COMMAND ${MAKE_SPI_IMAGE} ${bootstrap_BINARY_DIR}/bootstrap.bin ${baseband_BINARY_DIR}/baseband.img ${application_BINARY_DIR}/application.bin ${FIRMWARE_FILENAME}
DEPENDS bootstrap baseband application ${MAKE_SPI_IMAGE}
${bootstrap_BINARY_DIR}/bootstrap.bin ${baseband_BINARY_DIR}/baseband.img ${application_BINARY_DIR}/application.bin
VERBATIM
)
add_custom_target(
firmware
DEPENDS ${FIRMWARE_FILENAME}
)
add_custom_target(
program
COMMAND dfu-util --device 1fc9:000c --download ${HACKRF_FIRMWARE_IMAGE} --reset
COMMAND sleep 1s
COMMAND hackrf_spiflash -w ${FIRMWARE_FILENAME}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${FIRMWARE_FILENAME}
)
# TODO: Bad hack to fix location of LICENSE file for tar.
add_custom_command(
OUTPUT ${FIRMWARE_NAME}-${GIT_REVISION}.tar.bz2 ${FIRMWARE_NAME}-${GIT_REVISION}.zip
COMMAND cp ${LICENSE_PATH} LICENSE
COMMAND cp ${HACKRF_FIRMWARE_IMAGE} ${HACKRF_FIRMWARE_FILENAME}
COMMAND tar -c -j -f ${FIRMWARE_NAME}-${GIT_REVISION}.tar.bz2 ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_FILENAME} LICENSE
COMMAND zip -9 -q ${FIRMWARE_NAME}-${GIT_REVISION}.zip ${FIRMWARE_FILENAME} ${HACKRF_FIRMWARE_FILENAME} LICENSE
COMMAND rm -f LICENSE ${HACKRF_FIRMWARE_FILENAME}
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${FIRMWARE_FILENAME} ${LICENSE_PATH} ${HACKRF_FIRMWARE_IMAGE}
VERBATIM
)
add_custom_target(
release
DEPENDS ${FIRMWARE_NAME}-${GIT_REVISION}.tar.bz2 ${FIRMWARE_NAME}-${GIT_REVISION}.zip
)

View File

@ -0,0 +1,334 @@
#
# Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
#
# This file is part of PortaPack.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
##############################################################################
# Build global options
# NOTE: Can be overridden externally.
#
enable_language(C CXX ASM)
project(application)
# Compiler options here.
set(USE_OPT "-Os --specs=nano.specs")
# C specific options here (added to USE_OPT).
set(USE_COPT "-std=gnu99")
# C++ specific options here (added to USE_OPT).
set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions")
# Enable this if you want the linker to remove unused code and data
set(USE_LINK_GC yes)
# Linker extra options here.
set(USE_LDOPT)
# Enable this if you want link time optimizations (LTO)
set(USE_LTO no)
# If enabled, this option allows to compile the application in THUMB mode.
set(USE_THUMB yes)
# Enable this if you want to see the full log while compiling.
set(USE_VERBOSE_COMPILE no)
#
# Build global options
##############################################################################
##############################################################################
# Architecture or project specific options
#
# Enables the use of FPU on Cortex-M4 (no, softfp, hard).
set(USE_FPU no)
#
# Architecture or project specific options
##############################################################################
##############################################################################
# Project, sources and paths
#
set(CPLD_SVF_PATH ${HARDWARE_PATH}/portapack_h1/cpld/output_files/portapack_h1_cpld.svf)
set(CPLD_DATA_CPP ${CMAKE_CURRENT_BINARY_DIR}/portapack_cpld_data.cpp)
set(HACKRF_CPLD_DATA_HPP ${CMAKE_CURRENT_BINARY_DIR}/hackrf_cpld_data.hpp)
set(HACKRF_CPLD_DATA_CPP ${CMAKE_CURRENT_BINARY_DIR}/hackrf_cpld_data.cpp)
# Imported source files and paths
include(${CHIBIOS_PORTAPACK}/boards/GSG_HACKRF_ONE/board.cmake)
include(${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx_M0/platform.cmake)
include(${CHIBIOS}/os/hal/hal.cmake)
include(${CHIBIOS_PORTAPACK}/os/ports/GCC/ARMCMx/LPC43xx_M0/port.cmake)
include(${CHIBIOS}/os/kernel/kernel.cmake)
include(${CHIBIOS_PORTAPACK}/os/various/fatfs_bindings/fatfs.cmake)
include(${CHIBIOS}/test/test.cmake)
# Define linker script file here
set(LDSCRIPT ${PORTLD}/LPC43xx_M0.ld)
# C sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
set(CSRC
${PORTSRC}
${KERNSRC}
${TESTSRC}
${HALSRC}
${PLATFORMSRC}
${BOARDSRC}
${FATFSSRC}
)
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
set(CPPSRC
main.cpp
irq_lcd_frame.cpp
irq_controls.cpp
irq_rtc.cpp
${COMMON}/event.cpp
event_m0.cpp
${COMMON}/message_queue.cpp
${COMMON}/hackrf_hal.cpp
portapack.cpp
${COMMON}/portapack_shared_memory.cpp
baseband_api.cpp
${COMMON}/portapack_persistent_memory.cpp
${COMMON}/portapack_io.cpp
${COMMON}/i2c_pp.cpp
spi_pp.cpp
clock_manager.cpp
si5351.cpp
${COMMON}/wm8731.cpp
radio.cpp
baseband_cpld.cpp
tuning.cpp
rf_path.cpp
rffc507x.cpp
rffc507x_spi.cpp
max2837.cpp
max5864.cpp
debounce.cpp
touch.cpp
touch_adc.cpp
encoder.cpp
audio.cpp
${COMMON}/lcd_ili9341.cpp
${COMMON}/ui.cpp
${COMMON}/ui_text.cpp
${COMMON}/ui_widget.cpp
${COMMON}/ui_painter.cpp
${COMMON}/ui_focus.cpp
ui_navigation.cpp
ui_menu.cpp
ui_rssi.cpp
ui_channel.cpp
ui_audio.cpp
ui_font_fixed_8x16.cpp
ui_setup.cpp
ui_debug.cpp
ui_baseband_stats_view.cpp
ui_sd_card_status_view.cpp
ui_sd_card_debug.cpp
ui_console.cpp
ui_receiver.cpp
ui_record_view.cpp
ui_spectrum.cpp
recent_entries.cpp
receiver_model.cpp
spectrum_color_lut.cpp
analog_audio_app.cpp
${COMMON}/ais_baseband.cpp
${COMMON}/ais_packet.cpp
ais_app.cpp
tpms_app.cpp
${COMMON}/tpms_packet.cpp
ert_app.cpp
${COMMON}/ert_packet.cpp
capture_app.cpp
sd_card.cpp
time.cpp
file.cpp
log_file.cpp
${COMMON}/png_writer.cpp
capture_thread.cpp
${COMMON}/manchester.cpp
string_format.cpp
temperature_logger.cpp
${COMMON}/utility.cpp
${COMMON}/chibios_cpp.cpp
${COMMON}/debug.cpp
${COMMON}/gcc.cpp
${COMMON}/lfsr_random.cpp
core_control.cpp
${COMMON}/cpld_max5.cpp
${COMMON}/cpld_xilinx.cpp
${COMMON}/jtag.cpp
${COMMON}/jtag_tap.cpp
cpld_update.cpp
${CPLD_DATA_CPP}
${HACKRF_CPLD_DATA_CPP}
)
# C sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
set(ACSRC)
# C++ sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
set(ACPPSRC)
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
set(TCSRC)
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
set(TCPPSRC)
# List ASM source files here
set(ASMSRC ${PORTASM})
set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC}
${HALINC} ${PLATFORMINC} ${BOARDINC}
${FATFSINC}
${CHIBIOS}/os/various
)
#
# Project, sources and paths
##############################################################################
##############################################################################
# Compiler settings
#
# TODO: Entertain using MCU=cortex-m0.small-multiply for LPC43xx M0 core.
# However, on GCC-ARM-Embedded 4.9 2015q2, it seems to produce non-functional
# binaries.
set(MCU cortex-m0)
# ARM-specific options here
set(AOPT)
# THUMB-specific options here
set(TOPT "-mthumb -DTHUMB")
# Define C warning options here
set(CWARN "-Wall -Wextra -Wstrict-prototypes")
# Define C++ warning options here
set(CPPWARN "-Wall -Wextra")
#
# Compiler settings
##############################################################################
##############################################################################
# Start of default section
#
# List all default C defines here, like -D_DEBUG=1
# TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
# NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
set(DDEFS -DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -DGIT_REVISION=\"${GIT_REVISION}\")
# List all default ASM defines here, like -D_DEBUG=1
set(DADEFS)
# List all default directories to look for include files here
set(DINCDIR)
# List the default directory to look for the libraries here
set(DLIBDIR)
# List all default libraries here
set(DLIBS)
#
# End of default section
##############################################################################
##############################################################################
# Start of user section
#
# List all user C define here, like -D_DEBUG=1
set(UDEFS)
# Define ASM defines here
set(UADEFS)
# List all user directories here
set(UINCDIR)
# List the user directory to look for the libraries here
set(ULIBDIR)
# List all user libraries here
set(ULIBS)
#
# End of user defines
##############################################################################
set(RULESPATH ${CHIBIOS}/os/ports/GCC/ARMCMx)
include(${RULESPATH}/rules.cmake)
##############################################################################
add_custom_command(
OUTPUT ${CPLD_DATA_CPP}
COMMAND ${EXTRACT_CPLD_DATA} ${CPLD_SVF_PATH} >${CPLD_DATA_CPP}
DEPENDS ${EXTRACT_CPLD_DATA} ${CPLD_SVF_PATH}
)
add_custom_command(
OUTPUT ${HACKRF_CPLD_DATA_HPP} ${HACKRF_CPLD_DATA_CPP}
COMMAND ${EXTRACT_SVF_DATA_XC2C64A} ${HACKRF_CPLD_SVF_PATH} hackrf::one::cpld::verify_blocks ${HACKRF_CPLD_DATA_HPP} ${HACKRF_CPLD_DATA_CPP}
DEPENDS ${EXTRACT_SVF_DATA_XC2C64A} ${HACKRF_CPLD_SVF_PATH}
)
add_executable(${PROJECT_NAME}.elf ${CSRC} ${CPPSRC} ${ASMSRC})
set_target_properties(${PROJECT_NAME}.elf PROPERTIES LINK_DEPENDS ${LDSCRIPT})
add_definitions(${DEFS})
include_directories(. ${INCDIR})
link_directories(${LLIBDIR})
target_link_libraries(${PROJECT_NAME}.elf ${LIBS})
add_custom_command(
OUTPUT ${PROJECT_NAME}.bin
COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin
DEPENDS ${PROJECT_NAME}.elf
)
add_custom_target(
${PROJECT_NAME}
DEPENDS ${PROJECT_NAME}.bin
)

View File

@ -21,8 +21,6 @@
#include "ais_app.hpp"
#include "event_m0.hpp"
#include "string_format.hpp"
#include "baseband_api.hpp"
@ -128,25 +126,17 @@ static std::string true_heading(const TrueHeading value) {
} /* namespace format */
} /* namespace ais */
AISLogger::AISLogger(
const std::string& file_path
) : log_file { file_path }
{
}
void AISLogger::on_packet(const ais::Packet& packet) {
// TODO: Unstuff here, not in baseband!
if( log_file.is_open() ) {
std::string entry;
entry.reserve((packet.length() + 3) / 4);
std::string entry;
entry.reserve((packet.length() + 3) / 4);
for(size_t i=0; i<packet.length(); i+=4) {
const auto nibble = packet.read(i, 4);
entry += (nibble >= 10) ? ('W' + nibble) : ('0' + nibble);
}
log_file.write_entry(packet.received_at(), entry);
for(size_t i=0; i<packet.length(); i+=4) {
const auto nibble = packet.read(i, 4);
entry += (nibble >= 10) ? ('W' + nibble) : ('0' + nibble);
}
log_file.write_entry(packet.received_at(), entry);
}
void AISRecentEntry::update(const ais::Packet& packet) {
@ -296,6 +286,8 @@ void AISRecentEntryDetailView::set_entry(const AISRecentEntry& entry) {
}
AISAppView::AISAppView(NavigationView&) {
baseband::run_image(portapack::spi_flash::image_tag_ais);
add_children({ {
&label_channel,
&options_channel,
@ -305,16 +297,6 @@ AISAppView::AISAppView(NavigationView&) {
recent_entry_detail_view.hidden(true);
EventDispatcher::message_map().register_handler(Message::ID::AISPacket,
[this](Message* const p) {
const auto message = static_cast<const AISPacketMessage*>(p);
const ais::Packet packet { message->packet };
if( packet.is_valid() ) {
this->on_packet(packet);
}
}
);
target_frequency_ = initial_target_frequency;
radio::enable({
@ -326,12 +308,6 @@ AISAppView::AISAppView(NavigationView&) {
1,
});
baseband::start({
.mode = 3,
.sampling_rate = sampling_rate,
.decimation_factor = 1,
});
options_channel.on_change = [this](size_t, OptionsField::value_t v) {
this->on_frequency_changed(v);
};
@ -344,14 +320,16 @@ AISAppView::AISAppView(NavigationView&) {
this->on_show_list();
};
logger = std::make_unique<AISLogger>("ais.txt");
logger = std::make_unique<AISLogger>();
if( logger ) {
logger->append("ais.txt");
}
}
AISAppView::~AISAppView() {
baseband::stop();
radio::disable();
EventDispatcher::message_map().unregister_handler(Message::ID::AISPacket);
baseband::shutdown();
}
void AISAppView::focus() {

View File

@ -25,6 +25,8 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "event_m0.hpp"
#include "log_file.hpp"
#include "ais_packet.hpp"
@ -90,8 +92,10 @@ using AISRecentEntries = RecentEntries<ais::Packet, AISRecentEntry>;
class AISLogger {
public:
AISLogger(const std::string& file_path);
Optional<File::Error> append(const std::string& filename) {
return log_file.append(filename);
}
void on_packet(const ais::Packet& packet);
private:
@ -173,6 +177,17 @@ private:
}
};
MessageHandlerRegistration message_handler_packet {
Message::ID::AISPacket,
[this](Message* const p) {
const auto message = static_cast<const AISPacketMessage*>(p);
const ais::Packet packet { message->packet };
if( packet.is_valid() ) {
this->on_packet(packet);
}
}
};
uint32_t target_frequency_ = initial_target_frequency;
void on_packet(const ais::Packet& packet);

View File

@ -21,6 +21,8 @@
#include "analog_audio_app.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
@ -108,19 +110,10 @@ AnalogAudioView::AnalogAudioView(
this->on_show_options_frequency();
};
field_lna.set_value(receiver_model.lna());
field_lna.on_change = [this](int32_t v) {
this->on_lna_changed(v);
};
field_lna.on_show_options = [this]() {
this->on_show_options_rf_gain();
};
field_vga.set_value(receiver_model.vga());
field_vga.on_change = [this](int32_t v_db) {
this->on_vga_changed(v_db);
};
field_vga.on_show_options = [this]() {
this->on_show_options_rf_gain();
};
@ -139,6 +132,10 @@ AnalogAudioView::AnalogAudioView(
this->on_headphone_volume_changed(v);
};
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
audio::output::start();
update_modulation(static_cast<ReceiverModel::Mode>(modulation));
@ -150,6 +147,8 @@ AnalogAudioView::~AnalogAudioView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
void AnalogAudioView::on_hide() {
@ -178,18 +177,6 @@ void AnalogAudioView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) {
receiver_model.set_baseband_bandwidth(bandwidth_hz);
}
void AnalogAudioView::on_rf_amp_changed(bool v) {
receiver_model.set_rf_amp(v);
}
void AnalogAudioView::on_lna_changed(int32_t v_db) {
receiver_model.set_lna(v_db);
}
void AnalogAudioView::on_vga_changed(int32_t v_db) {
receiver_model.set_vga(v_db);
}
void AnalogAudioView::on_modulation_changed(const ReceiverModel::Mode modulation) {
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
// it's being shown or hidden.
@ -215,8 +202,11 @@ void AnalogAudioView::set_options_widget(std::unique_ptr<Widget> new_widget) {
if( new_widget ) {
options_widget = std::move(new_widget);
add_child(options_widget.get());
} else {
// TODO: Lame hack to hide options view due to my bad paint/damage algorithm.
options_widget = std::make_unique<Rectangle>(options_view_rect, style_options_group.background);
}
add_child(options_widget.get());
}
void AnalogAudioView::on_show_options_frequency() {
@ -238,11 +228,6 @@ void AnalogAudioView::on_show_options_frequency() {
void AnalogAudioView::on_show_options_rf_gain() {
auto widget = std::make_unique<RadioGainOptionsView>(options_view_rect, &style_options_group);
widget->set_rf_amp(receiver_model.rf_amp());
widget->on_change_rf_amp = [this](bool enable) {
this->on_rf_amp_changed(enable);
};
set_options_widget(std::move(widget));
field_lna.set_style(&style_options_group);
}
@ -286,6 +271,20 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
audio::output::mute();
record_view.stop();
baseband::shutdown();
portapack::spi_flash::image_tag_t image_tag;
switch(modulation) {
case ReceiverModel::Mode::AMAudio: image_tag = portapack::spi_flash::image_tag_am_audio; break;
case ReceiverModel::Mode::NarrowbandFMAudio: image_tag = portapack::spi_flash::image_tag_nfm_audio; break;
case ReceiverModel::Mode::WidebandFMAudio: image_tag = portapack::spi_flash::image_tag_wfm_audio; break;
case ReceiverModel::Mode::SpectrumAnalysis: image_tag = portapack::spi_flash::image_tag_wideband_spectrum; break;
default:
return;
}
baseband::run_image(image_tag);
const auto is_wideband_spectrum_mode = (modulation == ReceiverModel::Mode::SpectrumAnalysis);
receiver_model.set_baseband_configuration({
.mode = toUType(modulation),

View File

@ -143,16 +143,13 @@ private:
RecordView record_view {
{ 0 * 8, 2 * 16, 30 * 8, 1 * 16 },
"AUD_????", RecordView::FileType::WAV, 12, 2,
"AUD_????", RecordView::FileType::WAV, 4096, 4
};
spectrum::WaterfallWidget waterfall;
void on_tuning_frequency_changed(rf::Frequency f);
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
void on_rf_amp_changed(bool v);
void on_lna_changed(int32_t v_db);
void on_vga_changed(int32_t v_db);
void on_modulation_changed(const ReceiverModel::Mode modulation);
void on_show_options_frequency();
void on_show_options_rf_gain();

View File

@ -26,8 +26,18 @@
#include "portapack_shared_memory.hpp"
#include "core_control.hpp"
namespace baseband {
static void send_message(const Message* const message) {
// If message is only sent by this function via one thread, no need to check if
// another message is present before setting new message.
shared_memory.baseband_message = message;
creg::m0apptxevent::assert();
while(shared_memory.baseband_message);
}
void AMConfig::apply() const {
const AMConfigureMessage message {
taps_6k0_decim_0,
@ -37,7 +47,7 @@ void AMConfig::apply() const {
modulation,
audio_12k_hpf_300hz_config
};
shared_memory.baseband_queue.push(message);
send_message(&message);
audio::set_rate(audio::Rate::Hz_12000);
}
@ -51,7 +61,7 @@ void NBFMConfig::apply() const {
audio_24k_hpf_300hz_config,
audio_24k_deemph_300_6_config
};
shared_memory.baseband_queue.push(message);
send_message(&message);
audio::set_rate(audio::Rate::Hz_24000);
}
@ -64,26 +74,38 @@ void WFMConfig::apply() const {
audio_48k_hpf_30hz_config,
audio_48k_deemph_2122_6_config
};
shared_memory.baseband_queue.push(message);
send_message(&message);
audio::set_rate(audio::Rate::Hz_48000);
}
void start(BasebandConfiguration configuration) {
BasebandConfigurationMessage message { configuration };
shared_memory.baseband_queue.push(message);
}
static bool baseband_image_running = false;
void stop() {
shared_memory.baseband_queue.push_and_wait(
BasebandConfigurationMessage {
.configuration = { },
}
);
void run_image(const portapack::spi_flash::image_tag_t image_tag) {
if( baseband_image_running ) {
chDbgPanic("BBRunning");
}
creg::m4txevent::clear();
m4_init(image_tag, portapack::memory::map::m4_code);
baseband_image_running = true;
creg::m4txevent::enable();
}
void shutdown() {
ShutdownMessage shutdown_message;
shared_memory.baseband_queue.push(shutdown_message);
if( !baseband_image_running ) {
return;
}
creg::m4txevent::disable();
ShutdownMessage message;
send_message(&message);
shared_memory.application_queue.reset();
baseband_image_running = false;
}
void spectrum_streaming_start(size_t decimation_factor) {
@ -96,19 +118,27 @@ void spectrum_streaming_start(size_t decimation_factor) {
}
void spectrum_streaming_start() {
shared_memory.baseband_queue.push_and_wait(
SpectrumStreamingConfigMessage {
SpectrumStreamingConfigMessage::Mode::Running
}
);
SpectrumStreamingConfigMessage message {
SpectrumStreamingConfigMessage::Mode::Running
};
send_message(&message);
}
void spectrum_streaming_stop() {
shared_memory.baseband_queue.push_and_wait(
SpectrumStreamingConfigMessage {
SpectrumStreamingConfigMessage::Mode::Stopped
}
);
SpectrumStreamingConfigMessage message {
SpectrumStreamingConfigMessage::Mode::Stopped
};
send_message(&message);
}
void capture_start(CaptureConfig* const config) {
CaptureConfigMessage message { config };
send_message(&message);
}
void capture_stop() {
CaptureConfigMessage message { nullptr };
send_message(&message);
}
} /* namespace baseband */

View File

@ -26,6 +26,8 @@
#include "dsp_fir_taps.hpp"
#include "spi_image.hpp"
#include <cstddef>
namespace baseband {
@ -50,15 +52,16 @@ struct WFMConfig {
void apply() const;
};
void start(BasebandConfiguration configuration);
void stop();
void run_image(const portapack::spi_flash::image_tag_t image_tag);
void shutdown();
void spectrum_streaming_start(size_t decimation_factor);
void spectrum_streaming_start();
void spectrum_streaming_stop();
void capture_start(CaptureConfig* const config);
void capture_stop();
} /* namespace baseband */
#endif/*__BASEBAND_API_H__*/

View File

@ -21,59 +21,72 @@
#include "capture_app.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "portapack_persistent_memory.hpp"
using namespace portapack;
namespace ui {
CaptureAppView::CaptureAppView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_capture);
add_children({ {
&rssi,
&channel,
&field_frequency,
&field_frequency_step,
&field_rf_amp,
&field_lna,
&field_vga,
&record_view,
&waterfall,
} });
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_value(target_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
this->on_target_frequency_changed(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
auto new_view = nav.push<FrequencyKeypadView>(this->target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
this->on_target_frequency_changed(f);
this->field_frequency.set_value(f);
};
};
field_lna.set_value(receiver_model.lna());
field_lna.on_change = [this](int32_t v) {
this->on_lna_changed(v);
field_frequency_step.set_by_value(receiver_model.frequency_step());
field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) {
receiver_model.set_frequency_step(v);
this->field_frequency.set_step(v);
};
field_vga.set_value(receiver_model.vga());
field_vga.on_change = [this](int32_t v_db) {
this->on_vga_changed(v_db);
};
receiver_model.set_baseband_configuration({
.mode = toUType(ReceiverModel::Mode::Capture),
.sampling_rate = sampling_rate,
.decimation_factor = 1,
radio::enable({
tuning_frequency(),
sampling_rate,
baseband_bandwidth,
rf::Direction::Receive,
receiver_model.rf_amp(),
static_cast<int8_t>(receiver_model.lna()),
static_cast<int8_t>(receiver_model.vga()),
1,
});
receiver_model.set_baseband_bandwidth(baseband_bandwidth);
receiver_model.enable();
record_view.set_sampling_rate(sampling_rate / 8);
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
}
CaptureAppView::~CaptureAppView() {
receiver_model.disable();
radio::disable();
baseband::shutdown();
}
void CaptureAppView::on_hide() {
@ -94,16 +107,21 @@ void CaptureAppView::focus() {
record_view.focus();
}
void CaptureAppView::on_tuning_frequency_changed(rf::Frequency f) {
receiver_model.set_tuning_frequency(f);
void CaptureAppView::on_target_frequency_changed(rf::Frequency f) {
set_target_frequency(f);
}
void CaptureAppView::on_lna_changed(int32_t v_db) {
receiver_model.set_lna(v_db);
void CaptureAppView::set_target_frequency(const rf::Frequency new_value) {
persistent_memory::set_tuned_frequency(new_value);;
radio::set_tuning_frequency(tuning_frequency());
}
void CaptureAppView::on_vga_changed(int32_t v_db) {
receiver_model.set_vga(v_db);
rf::Frequency CaptureAppView::target_frequency() const {
return persistent_memory::tuned_frequency();
}
rf::Frequency CaptureAppView::tuning_frequency() const {
return target_frequency() - (sampling_rate / 4);
}
} /* namespace ui */

View File

@ -47,38 +47,49 @@ public:
std::string title() const override { return "Capture"; };
private:
static constexpr ui::Dim header_height = 3 * 16;
static constexpr ui::Dim header_height = 2 * 16;
static constexpr uint32_t sampling_rate = 4000000;
static constexpr uint32_t baseband_bandwidth = 2500000;
void on_tuning_frequency_changed(rf::Frequency f);
void on_lna_changed(int32_t v_db);
void on_vga_changed(int32_t v_db);
void on_target_frequency_changed(rf::Frequency f);
rf::Frequency target_frequency() const;
void set_target_frequency(const rf::Frequency new_value);
rf::Frequency tuning_frequency() const;
RSSI rssi {
{ 21 * 8, 0, 6 * 8, 4 },
{ 24 * 8, 0, 6 * 8, 4 },
};
Channel channel {
{ 21 * 8, 5, 6 * 8, 4 },
{ 24 * 8, 5, 6 * 8, 4 },
};
FrequencyField field_frequency {
{ 5 * 8, 0 * 16 },
{ 0 * 8, 0 * 16 },
};
FrequencyStepView field_frequency_step {
{ 10 * 8, 0 * 16 },
};
RFAmpField field_rf_amp {
{ 16 * 8, 0 * 16 }
};
LNAGainField field_lna {
{ 15 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 18 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 21 * 8, 0 * 16 }
};
RecordView record_view {
{ 0 * 8, 2 * 16, 30 * 8, 1 * 16 },
"BBD_????", RecordView::FileType::RawS16, 14, 1,
{ 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
"BBD_????", RecordView::FileType::RawS16, 16384, 3
};
spectrum::WaterfallWidget waterfall;

View File

@ -21,7 +21,7 @@
#include "capture_thread.hpp"
#include "portapack_shared_memory.hpp"
#include "baseband_api.hpp"
// StreamOutput ///////////////////////////////////////////////////////////
@ -31,36 +31,43 @@ public:
~StreamOutput();
size_t available() {
return fifo->len();
return fifo_buffers_full->len();
}
size_t read(void* const data, const size_t length) {
return fifo->out(reinterpret_cast<uint8_t*>(data), length);
StreamBuffer* get_buffer() {
StreamBuffer* p { nullptr };
fifo_buffers_full->out(p);
return p;
}
static FIFO<uint8_t>* fifo;
bool release_buffer(StreamBuffer* const p) {
p->empty();
return fifo_buffers_empty->in(p);
}
static FIFO<StreamBuffer*>* fifo_buffers_empty;
static FIFO<StreamBuffer*>* fifo_buffers_full;
private:
CaptureConfig* const config;
};
FIFO<uint8_t>* StreamOutput::fifo = nullptr;
FIFO<StreamBuffer*>* StreamOutput::fifo_buffers_empty = nullptr;
FIFO<StreamBuffer*>* StreamOutput::fifo_buffers_full = nullptr;
StreamOutput::StreamOutput(
CaptureConfig* const config
) : config { config }
{
shared_memory.baseband_queue.push_and_wait(
CaptureConfigMessage { config }
);
fifo = config->fifo;
baseband::capture_start(config);
fifo_buffers_empty = config->fifo_buffers_empty;
fifo_buffers_full = config->fifo_buffers_full;
}
StreamOutput::~StreamOutput() {
fifo = nullptr;
shared_memory.baseband_queue.push_and_wait(
CaptureConfigMessage { nullptr }
);
fifo_buffers_full = nullptr;
fifo_buffers_empty = nullptr;
baseband::capture_stop();
}
// CaptureThread //////////////////////////////////////////////////////////
@ -69,10 +76,14 @@ Thread* CaptureThread::thread = nullptr;
CaptureThread::CaptureThread(
std::unique_ptr<Writer> writer,
size_t write_size_log2,
size_t buffer_count_log2
) : config { write_size_log2, buffer_count_log2 },
writer { std::move(writer) }
size_t write_size,
size_t buffer_count,
std::function<void()> success_callback,
std::function<void(File::Error)> error_callback
) : config { write_size, buffer_count },
writer { std::move(writer) },
success_callback { std::move(success_callback) },
error_callback { std::move(error_callback) }
{
// Need significant stack for FATFS
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, CaptureThread::static_fn, this);
@ -81,7 +92,7 @@ CaptureThread::CaptureThread(
CaptureThread::~CaptureThread() {
if( thread ) {
chThdTerminate(thread);
chEvtSignal(thread, EVT_MASK_CAPTURE_THREAD);
chEvtSignal(thread, event_mask_loop_wake);
chThdWait(thread);
thread = nullptr;
}
@ -90,33 +101,42 @@ CaptureThread::~CaptureThread() {
void CaptureThread::check_fifo_isr() {
// TODO: Prevent over-signalling by transmitting a set of
// flags from the baseband core.
const auto fifo = StreamOutput::fifo;
const auto fifo = StreamOutput::fifo_buffers_full;
if( fifo ) {
chEvtSignalI(thread, EVT_MASK_CAPTURE_THREAD);
if( !fifo->is_empty() ) {
chEvtSignalI(thread, event_mask_loop_wake);
}
}
}
msg_t CaptureThread::run() {
const size_t write_size = 1U << config.write_size_log2;
const auto write_buffer = std::make_unique<uint8_t[]>(write_size);
if( !write_buffer ) {
return false;
msg_t CaptureThread::static_fn(void* arg) {
auto obj = static_cast<CaptureThread*>(arg);
const auto error = obj->run();
if( error.is_valid() && obj->error_callback ) {
obj->error_callback(error.value());
} else {
if( obj->success_callback ) {
obj->success_callback();
}
}
return 0;
}
Optional<File::Error> CaptureThread::run() {
StreamOutput stream { &config };
while( !chThdShouldTerminate() ) {
if( stream.available() >= write_size ) {
if( stream.read(write_buffer.get(), write_size) != write_size ) {
return false;
}
if( !writer->write(write_buffer.get(), write_size) ) {
return false;
if( stream.available() ) {
auto buffer = stream.get_buffer();
auto write_result = writer->write(buffer->data(), buffer->size());
if( write_result.is_error() ) {
return write_result.error();
}
stream.release_buffer(buffer);
} else {
chEvtWaitAny(EVT_MASK_CAPTURE_THREAD);
chEvtWaitAny(event_mask_loop_wake);
}
}
return true;
return { };
}

View File

@ -26,13 +26,16 @@
#include "event_m0.hpp"
#include "file.hpp"
#include "optional.hpp"
#include <cstdint>
#include <cstddef>
#include <utility>
class Writer {
public:
virtual bool write(const void* const buffer, const size_t bytes) = 0;
virtual File::Result<size_t> write(const void* const buffer, const size_t bytes) = 0;
virtual ~Writer() = default;
};
@ -40,8 +43,10 @@ class CaptureThread {
public:
CaptureThread(
std::unique_ptr<Writer> writer,
size_t write_size_log2,
size_t buffer_count_log2
size_t write_size,
size_t buffer_count,
std::function<void()> success_callback,
std::function<void(File::Error)> error_callback
);
~CaptureThread();
@ -52,16 +57,17 @@ public:
static void check_fifo_isr();
private:
static constexpr auto event_mask_loop_wake = EVENT_MASK(0);
CaptureConfig config;
std::unique_ptr<Writer> writer;
std::function<void()> success_callback;
std::function<void(File::Error)> error_callback;
static Thread* thread;
static msg_t static_fn(void* arg) {
auto obj = static_cast<CaptureThread*>(arg);
return obj->run();
}
static msg_t static_fn(void* arg);
msg_t run();
Optional<File::Error> run();
};
#endif/*__CAPTURE_THREAD_H__*/

View File

@ -41,17 +41,27 @@ char * modhash;
* I suppose I could force M4MEMMAP to an invalid memory reason which would
* cause an exception and effectively halt the M4. But that feels gross.
*/
void m4_init(const portapack::spi_flash::region_t from, const portapack::memory::region_t to) {
/* Initialize M4 code RAM */
std::memcpy(reinterpret_cast<void*>(to.base()), from.base(), from.size);
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to) {
const portapack::spi_flash::chunk_t* chunk = reinterpret_cast<const portapack::spi_flash::chunk_t*>(portapack::spi_flash::images.base());
while(chunk->tag) {
if( chunk->tag == image_tag ) {
/* Initialize M4 code RAM */
std::memcpy(reinterpret_cast<void*>(to.base()), &chunk->data[0], chunk->length);
/* M4 core is assumed to be sleeping with interrupts off, so we can mess
* with its address space and RAM without concern.
*/
LPC_CREG->M4MEMMAP = to.base();
/* M4 core is assumed to be sleeping with interrupts off, so we can mess
* with its address space and RAM without concern.
*/
LPC_CREG->M4MEMMAP = to.base();
/* Reset M4 core */
LPC_RGU->RESET_CTRL[0] = (1 << 13);
/* Reset M4 core */
LPC_RGU->RESET_CTRL[0] = (1 << 13);
return;
}
chunk = chunk->next();
}
chDbgPanic("NoImg");
}
void m4_request_shutdown() {

View File

@ -29,7 +29,7 @@
#include "spi_image.hpp"
#include "ui_navigation.hpp"
void m4_init(const portapack::spi_flash::region_t from, const portapack::memory::region_t to);
void m4_init(const portapack::spi_flash::image_tag_t image_tag, const portapack::memory::region_t to);
void m4_request_shutdown();
void m4_switch(const char * hash);
int m4_load_image(void);

View File

@ -21,11 +21,14 @@
#include "cpld_update.hpp"
#include "hackrf_gpio.hpp"
#include "portapack_hal.hpp"
#include "jtag_target_gpio.hpp"
#include "cpld_max5.hpp"
#include "cpld_xilinx.hpp"
#include "portapack_cpld_data.hpp"
#include "hackrf_cpld_data.hpp"
bool cpld_update_if_necessary() {
jtag::GPIOTarget target {
@ -82,3 +85,38 @@ bool cpld_update_if_necessary() {
return ok;
}
static jtag::GPIOTarget jtag_target_hackrf() {
return {
hackrf::one::gpio_cpld_tck,
hackrf::one::gpio_cpld_tms,
hackrf::one::gpio_cpld_tdi,
hackrf::one::gpio_cpld_tdo,
};
}
bool cpld_hackrf_load_sram() {
auto jtag_target_hackrf_cpld = jtag_target_hackrf();
cpld::xilinx::XC2C64A hackrf_cpld { jtag_target_hackrf_cpld };
hackrf_cpld.write_sram(hackrf::one::cpld::verify_blocks);
const auto ok = hackrf_cpld.verify_sram(hackrf::one::cpld::verify_blocks);
return ok;
}
bool cpld_hackrf_verify_eeprom() {
auto jtag_target_hackrf_cpld = jtag_target_hackrf();
cpld::xilinx::XC2C64A hackrf_cpld { jtag_target_hackrf_cpld };
const auto ok = hackrf_cpld.verify_eeprom(hackrf::one::cpld::verify_blocks);
return ok;
}
void cpld_hackrf_init_from_eeprom() {
auto jtag_target_hackrf_cpld = jtag_target_hackrf();
cpld::xilinx::XC2C64A hackrf_cpld { jtag_target_hackrf_cpld };
hackrf_cpld.init_from_eeprom();
}

View File

@ -24,4 +24,8 @@
bool cpld_update_if_necessary();
bool cpld_hackrf_load_sram();
bool cpld_hackrf_verify_eeprom();
void cpld_hackrf_init_from_eeprom();
#endif/*__CPLD_UPDATE_H__*/

View File

@ -21,8 +21,6 @@
#include "ert_app.hpp"
#include "event_m0.hpp"
#include "baseband_api.hpp"
#include "manchester.hpp"
@ -59,17 +57,9 @@ std::string commodity_type(CommodityType value) {
} /* namespace ert */
ERTLogger::ERTLogger(
const std::string& file_path
) : log_file { file_path }
{
}
void ERTLogger::on_packet(const ert::Packet& packet) {
if( log_file.is_open() ) {
const auto formatted = packet.symbols_formatted();
log_file.write_entry(packet.received_at(), formatted.data + "/" + formatted.errors);
}
const auto formatted = packet.symbols_formatted();
log_file.write_entry(packet.received_at(), formatted.data + "/" + formatted.errors);
}
const ERTRecentEntry::Key ERTRecentEntry::invalid_key { };
@ -131,18 +121,12 @@ void RecentEntriesView<ERTRecentEntries>::draw(
}
ERTAppView::ERTAppView(NavigationView&) {
baseband::run_image(portapack::spi_flash::image_tag_ert);
add_children({ {
&recent_entries_view,
} });
EventDispatcher::message_map().register_handler(Message::ID::ERTPacket,
[this](Message* const p) {
const auto message = static_cast<const ERTPacketMessage*>(p);
const ert::Packet packet { message->type, message->packet };
this->on_packet(packet);
}
);
radio::enable({
initial_target_frequency,
sampling_rate,
@ -152,20 +136,16 @@ ERTAppView::ERTAppView(NavigationView&) {
1,
});
baseband::start({
.mode = 6,
.sampling_rate = sampling_rate,
.decimation_factor = 1,
});
logger = std::make_unique<ERTLogger>("ert.txt");
logger = std::make_unique<ERTLogger>();
if( logger ) {
logger->append("ert.txt");
}
}
ERTAppView::~ERTAppView() {
baseband::stop();
radio::disable();
EventDispatcher::message_map().unregister_handler(Message::ID::ERTPacket);
baseband::shutdown();
}
void ERTAppView::focus() {

View File

@ -24,6 +24,8 @@
#include "ui_navigation.hpp"
#include "event_m0.hpp"
#include "log_file.hpp"
#include "ert_packet.hpp"
@ -85,8 +87,10 @@ struct ERTRecentEntry {
class ERTLogger {
public:
ERTLogger(const std::string& file_path);
Optional<File::Error> append(const std::string& filename) {
return log_file.append(filename);
}
void on_packet(const ert::Packet& packet);
private:
@ -124,6 +128,15 @@ private:
ERTRecentEntriesView recent_entries_view { recent };
MessageHandlerRegistration message_handler_packet {
Message::ID::ERTPacket,
[this](Message* const p) {
const auto message = static_cast<const ERTPacketMessage*>(p);
const ert::Packet packet { message->type, message->packet };
this->on_packet(packet);
}
};
void on_packet(const ert::Packet& packet);
void on_show_list();
};

View File

@ -22,8 +22,6 @@
#include "event_m0.hpp"
#include "portapack.hpp"
#include "portapack_shared_memory.hpp"
#include "portapack_persistent_memory.hpp"
#include "sd_card.hpp"
#include "time.hpp"
@ -42,6 +40,8 @@ using namespace lpc43xx;
#include <array>
#include "ui_font_fixed_8x16.hpp"
extern "C" {
CH_IRQ_HANDLER(M4Core_IRQHandler) {
@ -59,34 +59,60 @@ CH_IRQ_HANDLER(M4Core_IRQHandler) {
}
MessageHandlerMap EventDispatcher::message_map_;
class MessageHandlerMap {
public:
using MessageHandler = std::function<void(Message* const p)>;
void register_handler(const Message::ID id, MessageHandler&& handler) {
if( map_[toUType(id)] != nullptr ) {
chDbgPanic("MsgDblReg");
}
map_[toUType(id)] = std::move(handler);
}
void unregister_handler(const Message::ID id) {
map_[toUType(id)] = nullptr;
}
void send(Message* const message) {
if( message->id < Message::ID::MAX ) {
auto& fn = map_[toUType(message->id)];
if( fn ) {
fn(message);
}
}
}
private:
using MapType = std::array<MessageHandler, toUType(Message::ID::MAX)>;
MapType map_;
};
static MessageHandlerMap message_map;
Thread* EventDispatcher::thread_event_loop = nullptr;
bool EventDispatcher::is_running = false;
EventDispatcher::EventDispatcher(
ui::Widget* const top_widget,
ui::Painter& painter,
ui::Context& context
) : top_widget { top_widget },
painter(painter),
painter { },
context(context)
{
init_message_queues();
thread_event_loop = chThdSelf();
is_running = true;
touch_manager.on_event = [this](const ui::TouchEvent event) {
this->on_touch_event(event);
};
}
void EventDispatcher::run() {
creg::m4txevent::enable();
while(is_running) {
const auto events = wait();
dispatch(events);
}
creg::m4txevent::disable();
}
void EventDispatcher::request_stop() {
@ -100,7 +126,6 @@ void EventDispatcher::set_display_sleep(const bool sleep) {
portapack::io.lcd_backlight(false);
portapack::display.sleep();
} else {
portapack::bl_tick_counter = 0;
portapack::display.wake();
portapack::io.lcd_backlight(true);
}
@ -112,10 +137,48 @@ eventmask_t EventDispatcher::wait() {
}
void EventDispatcher::dispatch(const eventmask_t events) {
if( shared_memory.m4_panic_msg[0] != 0 ) {
halt = true;
}
if( halt ) {
if( shared_memory.m4_panic_msg[0] != 0 ) {
painter.fill_rectangle(
{ 0, 0, portapack::display.width(), portapack::display.height() },
ui::Color::red()
);
constexpr int border = 8;
painter.fill_rectangle(
{ border, border, portapack::display.width() - (border * 2), portapack::display.height() - (border * 2) },
ui::Color::black()
);
painter.draw_string({ 48, 24 }, top_widget->style(), "M4 Guru Meditation");
shared_memory.m4_panic_msg[sizeof(shared_memory.m4_panic_msg) - 1] = 0;
const std::string message = shared_memory.m4_panic_msg;
const int x_offset = (portapack::display.width() - (message.size() * 8)) / 2;
constexpr int y_offset = (portapack::display.height() - 16) / 2;
painter.draw_string(
{ x_offset, y_offset },
top_widget->style(),
message
);
shared_memory.m4_panic_msg[0] = 0;
}
return;
}
if( events & EVT_MASK_APPLICATION ) {
handle_application_queue();
}
if( events & EVT_MASK_LOCAL ) {
handle_local_queue();
}
if( events & EVT_MASK_RTC_TICK ) {
handle_rtc_tick();
}
@ -123,16 +186,16 @@ void EventDispatcher::dispatch(const eventmask_t events) {
if( events & EVT_MASK_SWITCHES ) {
handle_switches();
}
if( events & EVT_MASK_ENCODER ) {
handle_encoder();
}
if( !display_sleep ) {
if( events & EVT_MASK_LCD_FRAME_SYNC ) {
handle_lcd_frame_sync();
}
if( events & EVT_MASK_ENCODER ) {
handle_encoder();
}
if( events & EVT_MASK_TOUCH ) {
handle_touch();
}
@ -141,7 +204,13 @@ void EventDispatcher::dispatch(const eventmask_t events) {
void EventDispatcher::handle_application_queue() {
shared_memory.application_queue.handle([](Message* const message) {
message_map().send(message);
message_map.send(message);
});
}
void EventDispatcher::handle_local_queue() {
shared_memory.app_local_queue.handle([](Message* const message) {
message_map.send(message);
});
}
@ -170,7 +239,6 @@ ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent ev
for(const auto child : w->children()) {
const auto touched_widget = touch_widget(child, event);
if( touched_widget ) {
portapack::bl_tick_counter = 0;
return touched_widget;
}
}
@ -179,7 +247,6 @@ ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent ev
if( r.contains(event.point) ) {
if( w->on_touch(event) ) {
// This widget responded. Return it up the call stack.
portapack::bl_tick_counter = 0;
return w;
}
}
@ -209,7 +276,7 @@ void EventDispatcher::on_touch_event(ui::TouchEvent event) {
void EventDispatcher::handle_lcd_frame_sync() {
DisplayFrameSyncMessage message;
message_map().send(&message);
message_map.send(&message);
painter.paint_widget_tree(top_widget);
}
@ -275,11 +342,17 @@ void EventDispatcher::event_bubble_encoder(const ui::EncoderEvent event) {
}
void EventDispatcher::init_message_queues() {
new (&shared_memory.baseband_queue) MessageQueue(
shared_memory.baseband_queue_data, SharedMemory::baseband_queue_k
);
new (&shared_memory.application_queue) MessageQueue(
shared_memory.application_queue_data, SharedMemory::application_queue_k
);
new (&shared_memory) SharedMemory;
}
MessageHandlerRegistration::MessageHandlerRegistration(
const Message::ID message_id,
MessageHandlerMap::MessageHandler&& callback
) : message_id { message_id }
{
message_map.register_handler(message_id, std::move(callback));
}
MessageHandlerRegistration::~MessageHandlerRegistration() {
message_map.unregister_handler(message_id);
}

View File

@ -38,24 +38,19 @@
#include <cstdint>
constexpr auto EVT_MASK_RTC_TICK = EVENT_MASK(0);
constexpr auto EVT_MASK_LCD_FRAME_SYNC = EVENT_MASK(1);
constexpr auto EVT_MASK_SWITCHES = EVENT_MASK(3);
constexpr auto EVT_MASK_ENCODER = EVENT_MASK(4);
constexpr auto EVT_MASK_TOUCH = EVENT_MASK(5);
constexpr auto EVT_MASK_APPLICATION = EVENT_MASK(6);
constexpr auto EVT_MASK_CAPTURE_THREAD = EVENT_MASK(7);
class EventDispatcher {
public:
EventDispatcher(
ui::Widget* const top_widget,
ui::Painter& painter,
ui::Context& context
);
void run();
void request_stop();
static void request_stop();
void set_display_sleep(const bool sleep);
@ -65,6 +60,14 @@ public:
}
}
static inline void event_isr_rtc_tick() {
events_flag_isr(EVT_MASK_RTC_TICK);
}
static inline void event_isr_lcd_frame_sync() {
events_flag_isr(EVT_MASK_LCD_FRAME_SYNC);
}
static inline void events_flag(const eventmask_t events) {
if( thread_event_loop ) {
chEvtSignal(thread_event_loop, events);
@ -77,27 +80,35 @@ public:
}
}
static MessageHandlerMap& message_map() {
return message_map_;
template<typename T>
static void send_message(T& message) {
shared_memory.app_local_queue.push(message);
events_flag(EVT_MASK_LOCAL);
}
private:
static MessageHandlerMap message_map_;
static constexpr auto EVT_MASK_RTC_TICK = EVENT_MASK(0);
static constexpr auto EVT_MASK_LCD_FRAME_SYNC = EVENT_MASK(1);
static constexpr auto EVT_MASK_APPLICATION = EVENT_MASK(6);
static constexpr auto EVT_MASK_LOCAL = EVENT_MASK(7);
static Thread* thread_event_loop;
touch::Manager touch_manager;
ui::Widget* const top_widget;
ui::Painter& painter;
ui::Painter painter;
ui::Context& context;
uint32_t encoder_last = 0;
bool is_running = true;
static bool is_running;
bool sd_card_present = false;
bool display_sleep = false;
bool halt = false;
eventmask_t wait();
void dispatch(const eventmask_t events);
void handle_application_queue();
void handle_local_queue();
void handle_rtc_tick();
static ui::Widget* touch_widget(ui::Widget* const w, ui::TouchEvent event);
@ -117,4 +128,17 @@ private:
void init_message_queues();
};
class MessageHandlerRegistration {
public:
MessageHandlerRegistration(
const Message::ID message_id,
std::function<void(Message* const p)>&& callback
);
~MessageHandlerRegistration();
private:
const Message::ID message_id;
};
#endif/*__EVENT_M0_H__*/

View File

@ -23,65 +23,105 @@
#include <algorithm>
File::File(const std::string& filename, openmode mode) {
BYTE fatfs_mode = 0;
if( mode & openmode::in ) {
fatfs_mode |= FA_READ;
}
if( mode & openmode::out ) {
fatfs_mode |= FA_WRITE;
}
if( mode & openmode::trunc ) {
fatfs_mode |= FA_CREATE_ALWAYS;
}
if( mode & openmode::ate ) {
fatfs_mode |= FA_OPEN_ALWAYS;
}
/* Values added to FatFs FRESULT enum, values outside the FRESULT data type */
static_assert(sizeof(FIL::err) == 1, "FatFs FIL::err size not expected.");
#define FR_DISK_FULL (0x100)
#define FR_EOF (0x101)
#define FR_BAD_SEEK (0x102)
#define FR_UNEXPECTED (0x103)
if( f_open(&f, filename.c_str(), fatfs_mode) == FR_OK ) {
if( mode & openmode::ate ) {
if( f_lseek(&f, f_size(&f)) != FR_OK ) {
Optional<File::Error> File::open_fatfs(const std::string& filename, BYTE mode) {
auto result = f_open(&f, filename.c_str(), mode);
if( result == FR_OK ) {
if( mode & FA_OPEN_ALWAYS ) {
const auto result = f_lseek(&f, f_size(&f));
if( result != FR_OK ) {
f_close(&f);
}
}
}
if( result == FR_OK ) {
return { };
} else {
return { result };
}
}
Optional<File::Error> File::open(const std::string& filename) {
return open_fatfs(filename, FA_READ);
}
Optional<File::Error> File::append(const std::string& filename) {
return open_fatfs(filename, FA_WRITE | FA_OPEN_ALWAYS);
}
Optional<File::Error> File::create(const std::string& filename) {
return open_fatfs(filename, FA_WRITE | FA_CREATE_ALWAYS);
}
File::~File() {
f_close(&f);
}
bool File::read(void* const data, const size_t bytes_to_read) {
File::Result<size_t> File::read(void* const data, const size_t bytes_to_read) {
UINT bytes_read = 0;
const auto result = f_read(&f, data, bytes_to_read, &bytes_read);
return (result == FR_OK) && (bytes_read == bytes_to_read);
if( result == FR_OK ) {
return { static_cast<size_t>(bytes_read) };
} else {
return { static_cast<Error>(result) };
}
}
bool File::write(const void* const data, const size_t bytes_to_write) {
File::Result<size_t> File::write(const void* const data, const size_t bytes_to_write) {
UINT bytes_written = 0;
const auto result = f_write(&f, data, bytes_to_write, &bytes_written);
return (result == FR_OK) && (bytes_written == bytes_to_write);
if( result == FR_OK ) {
if( bytes_to_write == bytes_written ) {
return { static_cast<size_t>(bytes_written) };
} else {
return Error { FR_DISK_FULL };
}
} else {
return { static_cast<Error>(result) };
}
}
uint64_t File::seek(const uint64_t new_position) {
File::Result<uint64_t> File::seek(const uint64_t new_position) {
/* NOTE: Returns *old* position, not new position */
const auto old_position = f_tell(&f);
if( f_lseek(&f, new_position) != FR_OK ) {
f_close(&f);
const auto result = f_lseek(&f, new_position);
if( result != FR_OK ) {
return { static_cast<Error>(result) };
}
if( f_tell(&f) != new_position ) {
f_close(&f);
return { static_cast<Error>(FR_BAD_SEEK) };
}
return old_position;
return { static_cast<uint64_t>(old_position) };
}
bool File::puts(const std::string& string) {
const auto result = f_puts(string.c_str(), &f);
return (result >= 0);
Optional<File::Error> File::write_line(const std::string& s) {
const auto result_s = write(s.c_str(), s.size());
if( result_s.is_error() ) {
return { result_s.error() };
}
const auto result_crlf = write("\r\n", 2);
if( result_crlf.is_error() ) {
return { result_crlf.error() };
}
return { };
}
bool File::sync() {
Optional<File::Error> File::sync() {
const auto result = f_sync(&f);
return (result == FR_OK);
if( result == FR_OK ) {
return { };
} else {
return { result };
}
}
static std::string find_last_file_matching_pattern(const std::string& pattern) {
@ -140,6 +180,36 @@ std::string next_filename_stem_matching_pattern(const std::string& filename_stem
namespace std {
namespace filesystem {
std::string filesystem_error::what() const {
switch(err) {
case FR_OK: return "ok";
case FR_DISK_ERR: return "disk error";
case FR_INT_ERR: return "insanity detected";
case FR_NOT_READY: return "not ready";
case FR_NO_FILE: return "no file";
case FR_NO_PATH: return "no path";
case FR_INVALID_NAME: return "invalid name";
case FR_DENIED: return "denied";
case FR_EXIST: return "exists";
case FR_INVALID_OBJECT: return "invalid object";
case FR_WRITE_PROTECTED: return "write protected";
case FR_INVALID_DRIVE: return "invalid drive";
case FR_NOT_ENABLED: return "not enabled";
case FR_NO_FILESYSTEM: return "no filesystem";
case FR_MKFS_ABORTED: return "mkfs aborted";
case FR_TIMEOUT: return "timeout";
case FR_LOCKED: return "locked";
case FR_NOT_ENOUGH_CORE: return "not enough core";
case FR_TOO_MANY_OPEN_FILES: return "too many open files";
case FR_INVALID_PARAMETER: return "invalid parameter";
case FR_EOF: return "end of file";
case FR_DISK_FULL: return "disk full";
case FR_BAD_SEEK: return "bad seek";
case FR_UNEXPECTED: return "unexpected";
default: return "unknown";
}
}
directory_iterator::directory_iterator(
const char* path,
const char* wild
@ -164,5 +234,24 @@ bool is_regular_file(const file_status s) {
return !(s & AM_DIR);
}
space_info space(const path& p) {
DWORD free_clusters { 0 };
FATFS* fs;
if( f_getfree(p.c_str(), &free_clusters, &fs) == FR_OK ) {
#if _MAX_SS != _MIN_SS
static_assert(false, "FatFs not configured for fixed sector size");
#else
const std::uintmax_t cluster_bytes = fs->csize * _MIN_SS;
return {
(fs->n_fatent - 2) * cluster_bytes,
free_clusters * cluster_bytes,
free_clusters * cluster_bytes,
};
#endif
} else {
return { 0, 0, 0 };
}
}
} /* namespace filesystem */
} /* namespace std */

View File

@ -24,59 +24,59 @@
#include "ff.h"
#include "optional.hpp"
#include <cstddef>
#include <cstdint>
#include <string>
#include <array>
#include <memory>
#include <iterator>
class File {
public:
enum openmode {
app = 0x100,
binary = 0x200,
in = FA_READ,
out = FA_WRITE,
trunc = FA_CREATE_ALWAYS,
ate = FA_OPEN_ALWAYS,
};
File(const std::string& filename, openmode mode);
~File();
bool is_open() const {
return f_error(&f) == 0;
}
bool read(void* const data, const size_t bytes_to_read);
bool write(const void* const data, const size_t bytes_to_write);
uint64_t seek(const uint64_t new_position);
template<size_t N>
bool write(const std::array<uint8_t, N>& data) {
return write(data.data(), N);
}
bool puts(const std::string& string);
bool sync();
private:
FIL f;
};
inline constexpr File::openmode operator|(File::openmode a, File::openmode b) {
return File::openmode(static_cast<int>(a) | static_cast<int>(b));
}
std::string next_filename_stem_matching_pattern(const std::string& filename_stem_pattern);
namespace std {
namespace filesystem {
struct filesystem_error {
constexpr filesystem_error(
) : err { FR_OK }
{
}
constexpr filesystem_error(
FRESULT fatfs_error
) : err { fatfs_error }
{
}
constexpr filesystem_error(
unsigned int other_error
) : err { other_error }
{
}
uint32_t code() const {
return err;
}
std::string what() const;
private:
uint32_t err;
};
using path = std::string;
using file_status = BYTE;
struct space_info {
static_assert(sizeof(std::uintmax_t) >= 8, "std::uintmax_t too small (<uint64_t)");
std::uintmax_t capacity;
std::uintmax_t free;
std::uintmax_t available;
};
struct directory_entry : public FILINFO {
file_status status() const {
return fattrib;
@ -126,7 +126,96 @@ inline bool operator!=(const directory_iterator& lhs, const directory_iterator&
bool is_regular_file(const file_status s);
space_info space(const path& p);
} /* namespace filesystem */
} /* namespace std */
class File {
public:
using Error = std::filesystem::filesystem_error;
template<typename T>
struct Result {
enum class Type {
Success,
Error,
} type;
union {
T value_;
Error error_;
};
bool is_ok() const {
return type == Type::Success;
}
bool is_error() const {
return type == Type::Error;
}
const T& value() const {
return value_;
}
Error error() const {
return error_;
}
Result() = delete;
constexpr Result(
T value
) : type { Type::Success },
value_ { value }
{
}
constexpr Result(
Error error
) : type { Type::Error },
error_ { error }
{
}
~Result() {
if( type == Type::Success ) {
value_.~T();
}
}
};
File() { };
~File();
/* Prevent copies */
File(const File&) = delete;
File& operator=(const File&) = delete;
// TODO: Return Result<>.
Optional<Error> open(const std::string& filename);
Optional<Error> append(const std::string& filename);
Optional<Error> create(const std::string& filename);
Result<size_t> read(void* const data, const size_t bytes_to_read);
Result<size_t> write(const void* const data, const size_t bytes_to_write);
Result<uint64_t> seek(const uint64_t new_position);
template<size_t N>
Result<size_t> write(const std::array<uint8_t, N>& data) {
return write(data.data(), N);
}
Optional<Error> write_line(const std::string& s);
// TODO: Return Result<>.
Optional<Error> sync();
private:
FIL f;
Optional<Error> open_fatfs(const std::string& filename, BYTE mode);
};
#endif/*__FILE_H__*/

View File

@ -168,8 +168,6 @@ void timer0_callback(GPTDriver* const) {
EventDispatcher::events_flag_isr(event_mask);
chSysUnlockFromIsr();
}
touch::adc::start();
}
/* TODO: Refactor some/all of this to appropriate shared headers? */
@ -187,6 +185,8 @@ static GPTConfig timer0_config {
};
void controls_init() {
touch::adc::start();
/* GPT timer 0 is used to scan user interface controls -- touch screen,
* navigation switches.
*/

View File

@ -54,7 +54,7 @@ CH_IRQ_HANDLER(PIN_INT4_IRQHandler) {
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
EventDispatcher::events_flag_isr(EVT_MASK_LCD_FRAME_SYNC);
EventDispatcher::event_isr_lcd_frame_sync();
chSysUnlockFromIsr();
LPC_GPIO_INT->IST = (1U << 4);

View File

@ -39,7 +39,7 @@ CH_IRQ_HANDLER(RTC_IRQHandler) {
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
EventDispatcher::events_flag_isr(EVT_MASK_RTC_TICK);
EventDispatcher::event_isr_rtc_tick();
chSysUnlockFromIsr();
rtc::interrupt::clear_all();

View File

@ -23,21 +23,15 @@
#include "string_format.hpp"
LogFile::LogFile(
const std::string& file_path
) : file { file_path, File::openmode::out | File::openmode::ate }
{
}
bool LogFile::is_open() const {
return file.is_open();
}
bool LogFile::write_entry(const rtc::RTC& datetime, const std::string& entry) {
Optional<File::Error> LogFile::write_entry(const rtc::RTC& datetime, const std::string& entry) {
std::string timestamp = to_string_timestamp(datetime);
return write(timestamp + " " + entry + "\r\n");
return write_line(timestamp + " " + entry);
}
bool LogFile::write(const std::string& message) {
return file.puts(message) && file.sync();
Optional<File::Error> LogFile::write_line(const std::string& message) {
auto error = file.write_line(message);
if( !error.is_valid() ) {
file.sync();
}
return error;
}

View File

@ -31,16 +31,16 @@ using namespace lpc43xx;
class LogFile {
public:
LogFile(const std::string& file_path);
Optional<File::Error> append(const std::string& filename) {
return file.append(filename);
}
bool is_open() const;
bool write_entry(const rtc::RTC& datetime, const std::string& entry);
Optional<File::Error> write_entry(const rtc::RTC& datetime, const std::string& entry);
private:
File file;
bool write(const std::string& message);
Optional<File::Error> write_line(const std::string& message);
};
#endif/*__LOG_FILE_H__*/

View File

@ -59,8 +59,6 @@
#include "portapack.hpp"
#include "portapack_shared_memory.hpp"
#include "cpld_update.hpp"
#include "message_queue.hpp"
#include "ui.hpp"
@ -86,50 +84,43 @@
#include <string.h>
static void event_loop() {
ui::Context context;
ui::SystemView system_view {
context,
portapack::display.screen_rect()
};
EventDispatcher event_dispatcher { &system_view, context };
MessageHandlerRegistration message_handler_display_sleep {
Message::ID::DisplaySleep,
[&event_dispatcher](const Message* const) {
event_dispatcher.set_display_sleep(true);
}
};
event_dispatcher.run();
}
int main(void) {
portapack::init();
if( !cpld_update_if_necessary() ) {
chSysHalt();
}
portapack::io.init();
portapack::display.init();
sdcStart(&SDCD1, nullptr);
ui::Context context;
ui::SystemView system_view {
context,
portapack::display.screen_rect()
};
ui::Painter painter;
EventDispatcher event_dispatcher { &system_view, painter, context };
EventDispatcher::message_map().register_handler(Message::ID::Shutdown,
[&event_dispatcher](const Message* const) {
event_dispatcher.request_stop();
}
);
EventDispatcher::message_map().register_handler(Message::ID::DisplaySleep,
[&event_dispatcher](const Message* const) {
event_dispatcher.set_display_sleep(true);
}
);
m4_init(portapack::spi_flash::baseband, portapack::memory::map::m4_code);
controls_init();
lcd_frame_sync_configure();
rtc_interrupt_enable();
event_dispatcher.run();
event_loop();
sdcDisconnect(&SDCD1);
sdcStop(&SDCD1);
portapack::shutdown();
m4_init(portapack::spi_flash::hackrf, portapack::memory::map::m4_code_hackrf);
m4_init(portapack::spi_flash::image_tag_hackrf, portapack::memory::map::m4_code_hackrf);
m0_halt();
return 0;

View File

@ -32,6 +32,8 @@ using namespace hackrf::one;
#include "touch_adc.hpp"
#include "audio.hpp"
#include "cpld_update.hpp"
namespace portapack {
portapack::IO io {
@ -153,6 +155,14 @@ void init() {
radio::init();
touch::adc::init();
if( !cpld_update_if_necessary() ) {
chSysHalt();
}
if( !cpld_hackrf_load_sram() ) {
chSysHalt();
}
}
void shutdown() {
@ -160,6 +170,9 @@ void shutdown() {
radio::disable();
audio::shutdown();
cpld_hackrf_init_from_eeprom();
clock_manager.shutdown();
power.shutdown();

View File

@ -154,7 +154,6 @@ void ReceiverModel::enable() {
void ReceiverModel::disable() {
enabled_ = false;
update_antenna_bias();
baseband::stop();
// TODO: Responsibility for enabling/disabling the radio is muddy.
// Some happens in ReceiverModel, some inside radio namespace.
@ -225,13 +224,9 @@ void ReceiverModel::update_baseband_configuration() {
// protocols that need quick RX/TX turn-around.
// Disabling baseband while changing sampling rates seems like a good idea...
baseband::stop();
radio::set_baseband_rate(sampling_rate() * baseband_oversampling());
update_tuning_frequency();
radio::set_baseband_decimation_by(baseband_oversampling());
baseband::start(baseband_configuration);
}
void ReceiverModel::update_headphone_volume() {

View File

@ -30,6 +30,27 @@
#include "max2837.hpp"
#include "volume.hpp"
struct BasebandConfiguration {
int32_t mode;
uint32_t sampling_rate;
size_t decimation_factor;
constexpr BasebandConfiguration(
int32_t mode,
uint32_t sampling_rate,
size_t decimation_factor = 1
) : mode { mode },
sampling_rate { sampling_rate },
decimation_factor { decimation_factor }
{
}
constexpr BasebandConfiguration(
) : BasebandConfiguration { -1, 0, 1 }
{
}
};
class ReceiverModel {
public:
enum class Mode : int32_t {

View File

@ -70,26 +70,15 @@ void start() {
// static constexpr bool monitor_overruns_and_not_dones = false;
Samples get() {
const auto& frame = shared_memory.touch_adc_frame;
const auto xp = frame.dr[portapack::adc0_touch_xp_input];
const auto xn = frame.dr[portapack::adc0_touch_xn_input];
const auto yp = frame.dr[portapack::adc0_touch_yp_input];
const auto yn = frame.dr[portapack::adc0_touch_yn_input];
// if( monitor_overruns_and_not_dones ) {
// const auto dr_and = xp & xn & yp & yn;
// const auto dr_or = xp | xn | yp | yn;
// const bool done = (dr_and >> 31) & 1;
// const bool overrun = (dr_or >> 30) & 1;
// led_tx.write(overrun);
// led_rx.write(!done);
// }
const auto xp_reg = LPC_ADC0->DR[portapack::adc0_touch_xp_input];
const auto xn_reg = LPC_ADC0->DR[portapack::adc0_touch_xn_input];
const auto yp_reg = LPC_ADC0->DR[portapack::adc0_touch_yp_input];
const auto yn_reg = LPC_ADC0->DR[portapack::adc0_touch_yn_input];
return {
(xp >> 6) & 0x3ff,
(xn >> 6) & 0x3ff,
(yp >> 6) & 0x3ff,
(yn >> 6) & 0x3ff,
(xp_reg >> 6) & 0x3ff,
(xn_reg >> 6) & 0x3ff,
(yp_reg >> 6) & 0x3ff,
(yn_reg >> 6) & 0x3ff,
};
}

View File

@ -21,10 +21,11 @@
#include "tpms_app.hpp"
#include "event_m0.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "string_format.hpp"
#include "utility.hpp"
@ -49,26 +50,31 @@ std::string temperature(Temperature temperature) {
return to_string_dec_int(temperature.celsius(), 3);
}
std::string flags(Flags flags) {
return to_string_hex(flags, 2);
}
static std::string signal_type(SignalType signal_type) {
switch(signal_type) {
case SignalType::FSK_19k2_Schrader: return "FSK 38400 19200 Schrader";
case SignalType::OOK_8k192_Schrader: return "OOK - 8192 Schrader";
case SignalType::OOK_8k4_Schrader: return "OOK - 8400 Schrader";
default: return "- - - -";
}
}
} /* namespace format */
} /* namespace tpms */
TPMSLogger::TPMSLogger(
const std::string& file_path
) : log_file { file_path }
{
}
void TPMSLogger::on_packet(const tpms::Packet& packet, const uint32_t target_frequency) {
const auto hex_formatted = packet.symbols_formatted();
if( log_file.is_open() ) {
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
const auto tuning_frequency_str = to_string_dec_uint(target_frequency, 10);
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
const auto tuning_frequency_str = to_string_dec_uint(target_frequency, 10);
std::string entry = tuning_frequency_str + " FSK 38.4 19.2 " + hex_formatted.data + "/" + hex_formatted.errors;
log_file.write_entry(packet.received_at(), entry);
}
std::string entry = tuning_frequency_str + " " + tpms::format::signal_type(packet.signal_type()) + " " + hex_formatted.data + "/" + hex_formatted.errors;
log_file.write_entry(packet.received_at(), entry);
}
const TPMSRecentEntry::Key TPMSRecentEntry::invalid_key = { tpms::Reading::Type::None, 0 };
@ -82,16 +88,20 @@ void TPMSRecentEntry::update(const tpms::Reading& reading) {
if( reading.temperature().is_valid() ) {
last_temperature = reading.temperature();
}
if( reading.flags().is_valid() ) {
last_flags = reading.flags();
}
}
namespace ui {
static const std::array<std::pair<std::string, size_t>, 5> tpms_columns { {
static const std::array<std::pair<std::string, size_t>, 6> tpms_columns { {
{ "Tp", 2 },
{ "ID", 8 },
{ "kPa", 3 },
{ "C", 3 },
{ "Cnt", 3 },
{ "Fl", 2 },
} };
template<>
@ -143,63 +153,72 @@ void RecentEntriesView<TPMSRecentEntries>::draw(
line += " " + to_string_dec_uint(entry.received_count, 3);
}
if( entry.last_flags.is_valid() ) {
line += " " + tpms::format::flags(entry.last_flags.value());
} else {
line += " " " ";
}
line.resize(target_rect.width() / 8, ' ');
painter.draw_string(target_rect.pos, draw_style, line);
}
TPMSAppView::TPMSAppView(NavigationView&) {
baseband::run_image(portapack::spi_flash::image_tag_tpms);
add_children({ {
&rssi,
&channel,
&options_band,
&field_rf_amp,
&field_lna,
&field_vga,
&recent_entries_view,
} });
EventDispatcher::message_map().register_handler(Message::ID::TPMSPacket,
[this](Message* const p) {
const auto message = static_cast<const TPMSPacketMessage*>(p);
const tpms::Packet packet { message->packet };
this->on_packet(message->signal_type, packet);
}
);
radio::enable({
tuning_frequency(),
sampling_rate,
baseband_bandwidth,
rf::Direction::Receive,
false, 32, 32,
false,
static_cast<int8_t>(receiver_model.lna()),
static_cast<int8_t>(receiver_model.vga()),
1,
});
baseband::start({
.mode = 5,
.sampling_rate = sampling_rate,
.decimation_factor = 1,
});
options_band.on_change = [this](size_t, OptionsField::value_t v) {
this->on_band_changed(v);
};
options_band.set_by_value(target_frequency());
logger = std::make_unique<TPMSLogger>("tpms.txt");
logger = std::make_unique<TPMSLogger>();
if( logger ) {
logger->append("tpms.txt");
}
}
TPMSAppView::~TPMSAppView() {
baseband::stop();
radio::disable();
EventDispatcher::message_map().unregister_handler(Message::ID::TPMSPacket);
baseband::shutdown();
}
void TPMSAppView::focus() {
recent_entries_view.focus();
options_band.focus();
}
void TPMSAppView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
recent_entries_view.set_parent_rect({ 0, 0, new_parent_rect.width(), new_parent_rect.height() });
recent_entries_view.set_parent_rect({ 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height });
}
void TPMSAppView::on_packet(const tpms::SignalType signal_type, const tpms::Packet& packet) {
void TPMSAppView::on_packet(const tpms::Packet& packet) {
if( logger ) {
logger->on_packet(packet, target_frequency());
}
const auto reading_opt = packet.reading(signal_type);
const auto reading_opt = packet.reading();
if( reading_opt.is_valid() ) {
const auto reading = reading_opt.value();
recent.on_packet({ reading.type(), reading.id() }, reading);
@ -212,8 +231,17 @@ void TPMSAppView::on_show_list() {
recent_entries_view.focus();
}
void TPMSAppView::on_band_changed(const uint32_t new_band_frequency) {
set_target_frequency(new_band_frequency);
}
void TPMSAppView::set_target_frequency(const uint32_t new_value) {
target_frequency_ = new_value;
radio::set_tuning_frequency(tuning_frequency());
}
uint32_t TPMSAppView::target_frequency() const {
return initial_target_frequency;
return target_frequency_;
}
uint32_t TPMSAppView::tuning_frequency() const {

View File

@ -24,6 +24,11 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_rssi.hpp"
#include "ui_channel.hpp"
#include "event_m0.hpp"
#include "log_file.hpp"
@ -51,6 +56,7 @@ struct TPMSRecentEntry {
Optional<Pressure> last_pressure;
Optional<Temperature> last_temperature;
Optional<tpms::Flags> last_flags;
TPMSRecentEntry(
const Key& key
@ -70,7 +76,9 @@ using TPMSRecentEntries = RecentEntries<tpms::Reading, TPMSRecentEntry>;
class TPMSLogger {
public:
TPMSLogger(const std::string& file_path);
Optional<File::Error> append(const std::string& filename) {
return log_file.append(filename);
}
void on_packet(const tpms::Packet& packet, const uint32_t target_frequency);
@ -102,15 +110,61 @@ private:
static constexpr uint32_t sampling_rate = 2457600;
static constexpr uint32_t baseband_bandwidth = 1750000;
MessageHandlerRegistration message_handler_packet {
Message::ID::TPMSPacket,
[this](Message* const p) {
const auto message = static_cast<const TPMSPacketMessage*>(p);
const tpms::Packet packet { message->packet, message->signal_type };
this->on_packet(packet);
}
};
static constexpr ui::Dim header_height = 2 * 16;
RSSI rssi {
{ 21 * 8, 0, 6 * 8, 4 },
};
Channel channel {
{ 21 * 8, 5, 6 * 8, 4 },
};
OptionsField options_band {
{ 0 * 8, 0 * 16 },
3,
{
{ "315", 315000000 },
{ "434", 433920000 },
}
};
RFAmpField field_rf_amp {
{ 13 * 8, 0 * 16 }
};
LNAGainField field_lna {
{ 15 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 18 * 8, 0 * 16 }
};
TPMSRecentEntries recent;
std::unique_ptr<TPMSLogger> logger;
TPMSRecentEntriesView recent_entries_view { recent };
void on_packet(const tpms::SignalType signal_type, const tpms::Packet& packet);
uint32_t target_frequency_ = initial_target_frequency;
void on_packet(const tpms::Packet& packet);
void on_show_list();
void on_band_changed(const uint32_t new_band_frequency);
uint32_t target_frequency() const;
void set_target_frequency(const uint32_t new_value);
uint32_t tuning_frequency() const;
};

View File

@ -21,26 +21,12 @@
#include "ui_audio.hpp"
#include "event_m0.hpp"
#include "utility.hpp"
#include <algorithm>
namespace ui {
void Audio::on_show() {
EventDispatcher::message_map().register_handler(Message::ID::AudioStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const AudioStatisticsMessage*>(p)->statistics);
}
);
}
void Audio::on_hide() {
EventDispatcher::message_map().unregister_handler(Message::ID::AudioStatistics);
}
void Audio::paint(Painter& painter) {
const auto r = screen_rect();

View File

@ -26,6 +26,8 @@
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include <cstdint>
@ -42,15 +44,19 @@ public:
{
}
void on_show() override;
void on_hide() override;
void paint(Painter& painter) override;
private:
int32_t rms_db_;
int32_t max_db_;
MessageHandlerRegistration message_handler_statistics {
Message::ID::AudioStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const AudioStatisticsMessage*>(p)->statistics);
}
};
void on_statistics_update(const AudioStatistics& statistics);
};

View File

@ -21,8 +21,6 @@
#include "ui_baseband_stats_view.hpp"
#include "event_m0.hpp"
#include <string>
#include <algorithm>
@ -41,19 +39,6 @@ BasebandStatsView::BasebandStatsView() {
} });
}
void BasebandStatsView::on_show() {
EventDispatcher::message_map().register_handler(Message::ID::BasebandStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const BasebandStatisticsMessage*>(p)->statistics);
}
);
}
void BasebandStatsView::on_hide() {
EventDispatcher::message_map().unregister_handler(Message::ID::BasebandStatistics);
}
static std::string ticks_to_percent_string(const uint32_t ticks) {
constexpr size_t decimal_digits = 1;
constexpr size_t decimal_factor = decimal_digits * 10;

View File

@ -23,6 +23,9 @@
#define __UI_BASEBAND_STATS_VIEW_H__
#include "ui_widget.hpp"
#include "event_m0.hpp"
#include "message.hpp"
namespace ui {
@ -31,15 +34,19 @@ class BasebandStatsView : public View {
public:
BasebandStatsView();
void on_show() override;
void on_hide() override;
private:
Text text_stats {
{ 0 * 8, 0, (4 * 4 + 3) * 8, 1 * 16 },
"",
};
MessageHandlerRegistration message_handler_stats {
Message::ID::BasebandStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const BasebandStatisticsMessage*>(p)->statistics);
}
};
void on_statistics_update(const BasebandStatistics& statistics);
};

View File

@ -21,26 +21,12 @@
#include "ui_channel.hpp"
#include "event_m0.hpp"
#include "utility.hpp"
#include <algorithm>
namespace ui {
void Channel::on_show() {
EventDispatcher::message_map().register_handler(Message::ID::ChannelStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
}
);
}
void Channel::on_hide() {
EventDispatcher::message_map().unregister_handler(Message::ID::ChannelStatistics);
}
void Channel::paint(Painter& painter) {
const auto r = screen_rect();

View File

@ -26,6 +26,8 @@
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include <cstdint>
@ -41,14 +43,18 @@ public:
{
}
void on_show() override;
void on_hide() override;
void paint(Painter& painter) override;
private:
int32_t max_db_;
MessageHandlerRegistration message_handler_stats {
Message::ID::ChannelStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
}
};
void on_statistics_update(const ChannelStatistics& statistics);
};

View File

@ -88,7 +88,7 @@ SystemStatusView::SystemStatusView() {
button_sleep.on_select = [this](ImageButton&) {
DisplaySleepMessage message;
EventDispatcher::message_map().send(&message);
EventDispatcher::send_message(message);
};
}
@ -123,7 +123,11 @@ void SystemStatusView::on_camera() {
return;
}
PNGWriter png { filename_stem + ".PNG" };
PNGWriter png;
auto create_error = png.create(filename_stem + ".PNG");
if( create_error.is_valid() ) {
return;
}
for(int i=0; i<320; i++) {
std::array<ColorRGB888, 240> row;
@ -150,6 +154,10 @@ View* NavigationView::push_view(std::unique_ptr<View> new_view) {
}
void NavigationView::pop() {
if( view() == modal_view ) {
modal_view = nullptr;
}
// Can't pop last item from stack.
if( view_stack.size() > 1 ) {
free_view();
@ -160,6 +168,16 @@ void NavigationView::pop() {
}
}
void NavigationView::display_modal(
const std::string& title,
const std::string& message
) {
/* If a modal view is already visible, don't display another */
if( !modal_view ) {
modal_view = push<ModalMessageView>(title, message);
}
}
void NavigationView::free_view() {
remove_child(view());
}
@ -306,8 +324,8 @@ Context& SystemView::context() const {
/* HackRFFirmwareView ****************************************************/
HackRFFirmwareView::HackRFFirmwareView(NavigationView& nav) {
button_yes.on_select = [&nav](Button&){
m4_request_shutdown();
button_yes.on_select = [](Button&){
EventDispatcher::request_stop();
};
button_no.on_select = [&nav](Button&){
@ -404,4 +422,34 @@ void NotImplementedView::focus() {
button_done.focus();
}
/* ModalMessageView ******************************************************/
ModalMessageView::ModalMessageView(
NavigationView& nav,
const std::string& title,
const std::string& message
) : title_ { title }
{
button_done.on_select = [&nav](Button&){
nav.pop();
};
add_children({ {
&text_message,
&button_done,
} });
text_message.set(message);
const int text_message_width = message.size() * 8;
text_message.set_parent_rect({
(240 - text_message_width) / 2, 7 * 16,
text_message_width, 16
});
}
void ModalMessageView::focus() {
button_done.focus();
}
} /* namespace ui */

View File

@ -112,10 +112,13 @@ public:
void pop();
void display_modal(const std::string& title, const std::string& message);
void focus() override;
private:
std::vector<std::unique_ptr<View>> view_stack;
Widget* modal_view { nullptr };
Widget* view() const;
@ -256,6 +259,29 @@ private:
};
};
class ModalMessageView : public View {
public:
ModalMessageView(
NavigationView& nav,
const std::string& title,
const std::string& message
);
void focus() override;
std::string title() const override { return title_; };
private:
const std::string title_;
Text text_message { };
Button button_done {
{ 10 * 8, 13 * 16, 10 * 8, 24 },
"OK",
};
};
} /* namespace ui */
#endif/*__UI_NAVIGATION_H__*/

View File

@ -232,7 +232,7 @@ FrequencyOptionsView::FrequencyOptionsView(
{
set_style(style);
options_step.on_change = [this](size_t n, OptionsField::value_t v) {
field_step.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
this->on_step_changed(v);
};
@ -243,14 +243,14 @@ FrequencyOptionsView::FrequencyOptionsView(
add_children({ {
&text_step,
&options_step,
&field_step,
&field_ppm,
&text_ppm,
} });
}
void FrequencyOptionsView::set_step(rf::Frequency f) {
options_step.set_by_value(f);
field_step.set_by_value(f);
}
void FrequencyOptionsView::set_reference_ppm_correction(int32_t v) {
@ -269,6 +269,25 @@ void FrequencyOptionsView::on_reference_ppm_correction_changed(int32_t v) {
}
}
/* RFAmpField ************************************************************/
RFAmpField::RFAmpField(
Point parent_pos
) : NumberField {
parent_pos,
1,
{ 0, 1 },
1,
' ',
}
{
set_value(receiver_model.rf_amp());
on_change = [](int32_t v) {
receiver_model.set_rf_amp(v);
};
}
/* RadioGainOptionsView **************************************************/
RadioGainOptionsView::RadioGainOptionsView(
@ -282,20 +301,6 @@ RadioGainOptionsView::RadioGainOptionsView(
&label_rf_amp,
&field_rf_amp,
} });
field_rf_amp.on_change = [this](int32_t v) {
this->on_rf_amp_changed(v);
};
}
void RadioGainOptionsView::set_rf_amp(int32_t v_db) {
field_rf_amp.set_value(v_db);
}
void RadioGainOptionsView::on_rf_amp_changed(bool enable) {
if( on_change_rf_amp ) {
on_change_rf_amp(enable);
}
}
/* LNAGainField **********************************************************/
@ -309,6 +314,11 @@ LNAGainField::LNAGainField(
' ',
}
{
set_value(receiver_model.lna());
on_change = [](int32_t v) {
receiver_model.set_lna(v);
};
}
void LNAGainField::on_focus() {
@ -329,6 +339,11 @@ VGAGainField::VGAGainField(
' ',
}
{
set_value(receiver_model.vga());
on_change = [](int32_t v) {
receiver_model.set_vga(v);
};
}
void VGAGainField::on_focus() {

View File

@ -229,6 +229,32 @@ private:
void update_text();
};
class FrequencyStepView : public OptionsField {
public:
FrequencyStepView(
Point parent_pos
) : OptionsField {
parent_pos,
5,
{
{ " 100", 100 },
{ " 1k ", 1000 },
{ " 3k ", 3000 }, /* Approximate SSB bandwidth */
{ " 5k ", 5000 },
{ " 6k3", 6250 },
{ " 9k ", 9000 }, /* channel spacing for LF, MF in some regions */
{ " 10k ", 10000 },
{ " 12k5", 12500 },
{ " 25k ", 25000 },
{ "100k ", 100000 },
{ " 1M ", 1000000 },
{ " 10M ", 10000000 },
}
}
{
}
};
class FrequencyOptionsView : public View {
public:
std::function<void(rf::Frequency)> on_change_step;
@ -245,23 +271,8 @@ private:
"Step"
};
OptionsField options_step {
FrequencyStepView field_step {
{ 5 * 8, 0 * 16 },
5,
{
{ " 100", 100 },
{ " 1k ", 1000 },
{ " 3k ", 3000 }, /* Approximate SSB bandwidth */
{ " 5k ", 5000 },
{ " 6k3", 6250 },
{ " 9k ", 9000 }, /* channel spacing for LF, MF in some regions */
{ " 10k ", 10000 },
{ " 12k5", 12500 },
{ " 25k ", 25000 },
{ "100k ", 100000 },
{ " 1M ", 1000000 },
{ " 10M ", 10000000 },
}
};
void on_step_changed(rf::Frequency v);
@ -281,29 +292,24 @@ private:
};
};
class RFAmpField : public NumberField {
public:
RFAmpField(Point parent_pos);
};
class RadioGainOptionsView : public View {
public:
std::function<void(bool)> on_change_rf_amp;
RadioGainOptionsView(const Rect parent_rect, const Style* const style);
void set_rf_amp(int32_t v_db);
private:
Text label_rf_amp {
{ 0 * 8, 0 * 16, 3 * 8, 1 * 16 },
"Amp"
};
NumberField field_rf_amp {
RFAmpField field_rf_amp {
{ 4 * 8, 0 * 16},
1,
{ 0, 1 },
1,
' ',
};
void on_rf_amp_changed(bool enable);
};
class LNAGainField : public NumberField {

View File

@ -32,43 +32,61 @@ using namespace portapack;
#include <cstdint>
class RawFileWriter : public Writer {
class FileWriter : public Writer {
public:
RawFileWriter(
const std::string& filename
) : file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc }
{
FileWriter() = default;
FileWriter(const FileWriter&) = delete;
FileWriter& operator=(const FileWriter&) = delete;
FileWriter(FileWriter&& file) = delete;
FileWriter& operator=(FileWriter&&) = delete;
Optional<File::Error> create(const std::string& filename) {
return file.create(filename);
}
bool write(const void* const buffer, const size_t bytes) override {
return file.write(buffer, bytes);
File::Result<size_t> write(const void* const buffer, const size_t bytes) override {
auto write_result = file.write(buffer, bytes) ;
if( write_result.is_ok() ) {
bytes_written += write_result.value();
}
return write_result;
}
private:
protected:
File file;
uint64_t bytes_written { 0 };
};
class WAVFileWriter : public Writer {
using RawFileWriter = FileWriter;
class WAVFileWriter : public FileWriter {
public:
WAVFileWriter(
const std::string& filename,
size_t sampling_rate
) : file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc },
header { sampling_rate }
) : header { sampling_rate }
{
update_header();
}
WAVFileWriter(const WAVFileWriter&) = delete;
WAVFileWriter& operator=(const WAVFileWriter&) = delete;
WAVFileWriter(WAVFileWriter&&) = delete;
WAVFileWriter& operator=(WAVFileWriter&&) = delete;
~WAVFileWriter() {
update_header();
}
bool write(const void* const buffer, const size_t bytes) override {
const auto success = file.write(buffer, bytes) ;
if( success ) {
bytes_written += bytes;
Optional<File::Error> create(
const std::string& filename
) {
const auto create_error = FileWriter::create(filename);
if( create_error.is_valid() ) {
return create_error;
} else {
return update_header();
}
return success;
}
private:
@ -121,15 +139,24 @@ private:
data_t data;
};
File file;
header_t header;
uint64_t bytes_written { 0 };
void update_header() {
Optional<File::Error> update_header() {
header.set_data_size(bytes_written);
const auto old_position = file.seek(0);
file.write(&header, sizeof(header));
file.seek(old_position);
const auto seek_0_result = file.seek(0);
if( seek_0_result.is_error() ) {
return seek_0_result.error();
}
const auto old_position = seek_0_result.value();
const auto write_result = file.write(&header, sizeof(header));
if( write_result.is_error() ) {
return write_result.error();
}
const auto seek_old_result = file.seek(old_position);
if( seek_old_result.is_error() ) {
return seek_old_result.error();
}
return { };
}
};
@ -139,20 +166,24 @@ RecordView::RecordView(
const Rect parent_rect,
std::string filename_stem_pattern,
const FileType file_type,
const size_t buffer_size_k,
const size_t buffer_count_k
const size_t write_size,
const size_t buffer_count
) : View { parent_rect },
filename_stem_pattern { filename_stem_pattern },
file_type { file_type },
buffer_size_k { buffer_size_k },
buffer_count_k { buffer_count_k }
write_size { write_size },
buffer_count { buffer_count }
{
add_children({ {
&rect_background,
&button_record,
&text_record_filename,
&text_record_dropped,
&text_time_available,
} });
rect_background.set_parent_rect({ { 0, 0 }, size() });
button_record.on_select = [this](ImageButton&) {
this->toggle();
};
@ -170,6 +201,21 @@ void RecordView::focus() {
button_record.focus();
}
void RecordView::set_sampling_rate(const size_t new_sampling_rate) {
if( new_sampling_rate != sampling_rate ) {
stop();
sampling_rate = new_sampling_rate;
button_record.hidden(sampling_rate == 0);
text_record_filename.hidden(sampling_rate == 0);
text_record_dropped.hidden(sampling_rate == 0);
text_time_available.hidden(sampling_rate == 0);
rect_background.hidden(sampling_rate != 0);
update_status_display();
}
}
bool RecordView::is_active() const {
return (bool)capture_thread;
}
@ -200,17 +246,39 @@ void RecordView::start() {
std::unique_ptr<Writer> writer;
switch(file_type) {
case FileType::WAV:
writer = std::make_unique<WAVFileWriter>(
filename_stem + ".WAV",
sampling_rate
);
{
auto p = std::make_unique<WAVFileWriter>(
sampling_rate
);
auto create_error = p->create(
filename_stem + ".WAV"
);
if( create_error.is_valid() ) {
handle_error(create_error.value());
} else {
writer = std::move(p);
}
}
break;
case FileType::RawS16:
write_metadata_file(filename_stem + ".TXT");
writer = std::make_unique<RawFileWriter>(
filename_stem + ".C16"
);
{
const auto metadata_file_error = write_metadata_file(filename_stem + ".TXT");
if( metadata_file_error.is_valid() ) {
handle_error(metadata_file_error.value());
return;
}
auto p = std::make_unique<RawFileWriter>();
auto create_error = p->create(
filename_stem + ".C16"
);
if( create_error.is_valid() ) {
handle_error(create_error.value());
} else {
writer = std::move(p);
}
}
break;
default:
@ -222,9 +290,19 @@ void RecordView::start() {
button_record.set_bitmap(&bitmap_stop);
capture_thread = std::make_unique<CaptureThread>(
std::move(writer),
buffer_size_k, buffer_count_k
write_size, buffer_count,
[]() {
CaptureThreadDoneMessage message { };
EventDispatcher::send_message(message);
},
[](File::Error error) {
CaptureThreadDoneMessage message { error.code() };
EventDispatcher::send_message(message);
}
);
}
update_status_display();
}
void RecordView::stop() {
@ -232,20 +310,66 @@ void RecordView::stop() {
capture_thread.reset();
button_record.set_bitmap(&bitmap_record);
}
update_status_display();
}
void RecordView::write_metadata_file(const std::string& filename) {
File file { filename, File::openmode::out | File::openmode::trunc };
file.puts("sample_rate=" + to_string_dec_uint(sampling_rate) + "\n");
file.puts("center_frequency=" + to_string_dec_uint(receiver_model.tuning_frequency()) + "\n");
Optional<File::Error> RecordView::write_metadata_file(const std::string& filename) {
File file;
const auto create_error = file.create(filename);
if( create_error.is_valid() ) {
return create_error;
} else {
const auto error_line1 = file.write_line("sample_rate=" + to_string_dec_uint(sampling_rate));
if( error_line1.is_valid() ) {
return error_line1;
}
const auto error_line2 = file.write_line("center_frequency=" + to_string_dec_uint(receiver_model.tuning_frequency()));
if( error_line2.is_valid() ) {
return error_line2;
}
return { };
}
}
void RecordView::on_tick_second() {
update_status_display();
}
void RecordView::update_status_display() {
if( is_active() ) {
const auto dropped_percent = std::min(99U, capture_thread->state().dropped_percent());
const auto s = to_string_dec_uint(dropped_percent, 2, ' ') + "\%";
text_record_dropped.set(s);
}
if( sampling_rate ) {
const auto space_info = std::filesystem::space("");
const uint32_t bytes_per_second = file_type == FileType::WAV ? (sampling_rate * 2) : (sampling_rate * 4);
const uint32_t available_seconds = space_info.free / bytes_per_second;
const uint32_t seconds = available_seconds % 60;
const uint32_t available_minutes = available_seconds / 60;
const uint32_t minutes = available_minutes % 60;
const uint32_t hours = available_minutes / 60;
const std::string available_time =
to_string_dec_uint(hours, 3, ' ') + ":" +
to_string_dec_uint(minutes, 2, '0') + ":" +
to_string_dec_uint(seconds, 2, '0');
text_time_available.set(available_time);
}
}
void RecordView::handle_capture_thread_done(const File::Error error) {
stop();
if( error.code() ) {
handle_error(error);
}
}
void RecordView::handle_error(const File::Error error) {
if( on_error ) {
on_error(error.what());
}
}
} /* namespace ui */

View File

@ -37,6 +37,8 @@ namespace ui {
class RecordView : public View {
public:
std::function<void(std::string)> on_error;
enum FileType {
RawS16 = 2,
WAV = 3,
@ -46,19 +48,14 @@ public:
const Rect parent_rect,
std::string filename_stem_pattern,
FileType file_type,
const size_t buffer_size_k,
const size_t buffer_count_k
const size_t write_size,
const size_t buffer_count
);
~RecordView();
void focus() override;
void set_sampling_rate(const size_t new_sampling_rate) {
if( new_sampling_rate != sampling_rate ) {
stop();
sampling_rate = new_sampling_rate;
}
}
void set_sampling_rate(const size_t new_sampling_rate);
void start();
void stop();
@ -67,17 +64,25 @@ public:
private:
void toggle();
void write_metadata_file(const std::string& filename);
Optional<File::Error> write_metadata_file(const std::string& filename);
void on_tick_second();
void update_status_display();
void handle_capture_thread_done(const File::Error error);
void handle_error(const File::Error error);
const std::string filename_stem_pattern;
const FileType file_type;
const size_t buffer_size_k;
const size_t buffer_count_k;
const size_t write_size;
const size_t buffer_count;
size_t sampling_rate { 0 };
SignalToken signal_token_tick_second;
Rectangle rect_background {
Color::black()
};
ImageButton button_record {
{ 0 * 8, 0 * 16, 2 * 8, 1 * 16 },
&bitmap_record,
@ -95,7 +100,20 @@ private:
"",
};
Text text_time_available {
{ 21 * 8, 0 * 16, 9 * 8, 16 },
"",
};
std::unique_ptr<CaptureThread> capture_thread;
MessageHandlerRegistration message_handler_capture_thread_error {
Message::ID::CaptureThreadDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const CaptureThreadDoneMessage*>(p);
this->handle_capture_thread_done(message.error);
}
};
};
} /* namespace ui */

View File

@ -21,26 +21,12 @@
#include "ui_rssi.hpp"
#include "event_m0.hpp"
#include "utility.hpp"
#include <algorithm>
namespace ui {
void RSSI::on_show() {
EventDispatcher::message_map().register_handler(Message::ID::RSSIStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const RSSIStatisticsMessage*>(p)->statistics);
}
);
}
void RSSI::on_hide() {
EventDispatcher::message_map().unregister_handler(Message::ID::RSSIStatistics);
}
void RSSI::paint(Painter& painter) {
const auto r = screen_rect();

View File

@ -26,6 +26,8 @@
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include <cstdint>
@ -43,9 +45,6 @@ public:
{
}
void on_show() override;
void on_hide() override;
void paint(Painter& painter) override;
private:
@ -53,6 +52,13 @@ private:
int32_t avg_;
int32_t max_;
MessageHandlerRegistration message_handler_stats {
Message::ID::RSSIStatistics,
[this](const Message* const p) {
this->on_statistics_update(static_cast<const RSSIStatisticsMessage*>(p)->statistics);
}
};
void on_statistics_update(const RSSIStatistics& statistics);
};

View File

@ -27,6 +27,7 @@
#include "lfsr_random.hpp"
#include "ff.h"
#include "diskio.h"
#include "ch.h"
#include "hal.h"
@ -133,22 +134,24 @@ private:
return Result::FailHeap;
}
File file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc };
if( !file.is_open() ) {
File file;
auto file_create_error = file.create(filename);
if( file_create_error.is_valid() ) {
return Result::FailFileOpenWrite;
}
lfsr_word_t v = 1;
const halrtcnt_t test_start = halGetCounterValue();
while( !chThdShouldTerminate() && file.is_open() && (_stats.write_bytes < bytes_to_write) ) {
while( !chThdShouldTerminate() && (_stats.write_bytes < bytes_to_write) ) {
lfsr_fill(v,
reinterpret_cast<lfsr_word_t*>(buffer->data()),
sizeof(*buffer.get()) / sizeof(lfsr_word_t)
);
const halrtcnt_t write_start = halGetCounterValue();
if( !file.write(buffer->data(), buffer->size()) ) {
const auto result_write = file.write(buffer->data(), buffer->size());
if( result_write.is_error() ) {
break;
}
const halrtcnt_t write_end = halGetCounterValue();
@ -178,17 +181,19 @@ private:
return Result::FailHeap;
}
File file { filename, File::openmode::in | File::openmode::binary };
if( !file.is_open() ) {
File file;
auto file_open_error = file.open(filename);
if( file_open_error.is_valid() ) {
return Result::FailFileOpenRead;
}
lfsr_word_t v = 1;
const halrtcnt_t test_start = halGetCounterValue();
while( !chThdShouldTerminate() && file.is_open() && (_stats.read_bytes < bytes_to_read) ) {
while( !chThdShouldTerminate() && (_stats.read_bytes < bytes_to_read) ) {
const halrtcnt_t read_start = halGetCounterValue();
if( !file.read(buffer->data(), buffer->size()) ) {
const auto result_read = file.read(buffer->data(), buffer->size());
if( result_read.is_error() ) {
break;
}
const halrtcnt_t read_end = halGetCounterValue();
@ -227,14 +232,15 @@ namespace ui {
SDCardDebugView::SDCardDebugView(NavigationView& nav) {
add_children({ {
&text_title,
&text_detected_title,
&text_detected_value,
&text_csd_title,
&text_csd_value_3,
&text_csd_value_2,
&text_csd_value_1,
&text_csd_value_0,
&text_bus_width_title,
&text_bus_width_value,
&text_card_mode_title,
&text_card_mode_value,
// &text_csd_title,
// &text_csd_value,
&text_card_type_title,
&text_card_type_value,
&text_block_size_title,
&text_block_size_value,
&text_block_count_title,
@ -272,10 +278,33 @@ void SDCardDebugView::focus() {
button_ok.focus();
}
static std::string format_3dot3_string(const uint32_t value_in_thousandths) {
if( value_in_thousandths < 1000000U ) {
const uint32_t thousandths_part = value_in_thousandths % 1000;
const uint32_t integer_part = value_in_thousandths / 1000U;
return to_string_dec_uint(integer_part, 3) + "." + to_string_dec_uint(thousandths_part, 3, '0');
} else {
return "HHH.HHH";
}
}
static std::string format_bytes_size_string(uint64_t value) {
static const std::array<char, 5> suffix { { ' ', 'K', 'M', 'G', 'T' } };
size_t suffix_index = 1;
while( (value >= 1000000U) && (suffix_index < suffix.size()) ) {
value /= 1000U;
suffix_index++;
}
return format_3dot3_string(value) + " " + suffix[suffix_index] + "B";
}
void SDCardDebugView::on_status(const sd_card::Status) {
text_bus_width_value.set("");
text_card_mode_value.set("");
// text_csd_value.set("");
text_card_type_value.set("");
text_csd_value_0.set("");
text_csd_value_1.set("");
text_csd_value_2.set("");
text_csd_value_3.set("");
text_block_size_value.set("");
text_block_count_value.set("");
text_capacity_value.set("");
@ -285,8 +314,6 @@ void SDCardDebugView::on_status(const sd_card::Status) {
text_test_read_rate_value.set("");
const bool is_inserted = sdcIsCardInserted(&SDCD1);
text_detected_value.set(is_inserted ? "Yes" : " No");
if( is_inserted ) {
const auto card_width_flags = LPC_SDMMC->CTYPE & 0x10001;
size_t card_width = 0;
@ -298,56 +325,50 @@ void SDCardDebugView::on_status(const sd_card::Status) {
}
text_bus_width_value.set(card_width ? to_string_dec_uint(card_width, 1) : "X");
text_card_mode_value.set("0x" + to_string_hex(SDCD1.cardmode, 8));
// text_csd_value.set("0x" + to_string_hex(SDCD1.csd, 8));
// TODO: Implement Text class right-justify!
BYTE card_type;
disk_ioctl(0, MMC_GET_TYPE, &card_type);
std::string formatted_card_type;
switch(card_type & SDC_MODE_CARDTYPE_MASK) {
case SDC_MODE_CARDTYPE_SDV11: formatted_card_type = "SD V1.1"; break;
case SDC_MODE_CARDTYPE_SDV20: formatted_card_type = "SD V2.0"; break;
case SDC_MODE_CARDTYPE_MMC: formatted_card_type = "MMC"; break;
default: formatted_card_type = "???"; break;
}
if( card_type & SDC_MODE_HIGH_CAPACITY ) {
formatted_card_type += ", SDHC";
}
text_card_type_value.set(formatted_card_type);
std::array<uint32_t, 4> csd;
disk_ioctl(0, MMC_GET_CSD, csd.data());
text_csd_value_3.set(to_string_hex(csd[3], 8));
text_csd_value_2.set(to_string_hex(csd[2], 8));
text_csd_value_1.set(to_string_hex(csd[1], 8));
text_csd_value_0.set(to_string_hex(csd[0], 8));
BlockDeviceInfo block_device_info;
if( sdcGetInfo(&SDCD1, &block_device_info) == CH_SUCCESS ) {
text_block_size_value.set(to_string_dec_uint(block_device_info.blk_size, 5));
text_block_count_value.set(to_string_dec_uint(block_device_info.blk_num, 9));
const uint64_t capacity = block_device_info.blk_size * uint64_t(block_device_info.blk_num);
if( capacity >= 1000000000 ) {
const uint32_t capacity_mb = capacity / 1000000U;
const uint32_t fraction_gb = capacity_mb % 1000;
const uint32_t capacity_gb = capacity_mb / 1000U;
text_capacity_value.set(
to_string_dec_uint(capacity_gb, 3) + "." +
to_string_dec_uint(fraction_gb, 3, '0') + " GB"
);
} else {
const uint32_t capacity_kb = capacity / 1000U;
const uint32_t fraction_mb = capacity_kb % 1000;
const uint32_t capacity_mb = capacity_kb / 1000U;
text_capacity_value.set(
to_string_dec_uint(capacity_mb, 3) + "." +
to_string_dec_uint(fraction_mb, 3, '0') + " MB"
);
}
text_capacity_value.set(format_bytes_size_string(capacity));
}
}
}
static std::string format_ticks_as_ms(const halrtcnt_t value) {
const uint32_t us = uint64_t(value) * 1000000U / halGetCounterFrequency();
const uint32_t ms_frac = us % 1000U;
const uint32_t ms_int = us / 1000U;
if( ms_int < 1000 ) {
return to_string_dec_uint(ms_int, 3) + "." + to_string_dec_uint(ms_frac, 3, '0');
} else {
return "HHH.HHH";
}
return format_3dot3_string(us);
}
static std::string format_bytes_per_ticks_as_mib(const size_t bytes, const halrtcnt_t ticks) {
const uint32_t bps = uint64_t(bytes) * halGetCounterFrequency() / ticks;
const uint32_t kbps = bps / 1000U;
const uint32_t mbps_frac = kbps % 1000U;
const uint32_t mbps_int = kbps / 1000U;
if( mbps_int < 1000 ) {
return to_string_dec_uint(mbps_int, 3) + "." + to_string_dec_uint(mbps_frac, 3, '0');
} else {
return "HHH.HHH";
}
return format_3dot3_string(kbps);
}
void SDCardDebugView::on_test() {

View File

@ -49,15 +49,28 @@ private:
"SD Card",
};
static constexpr size_t detected_characters = 3;
Text text_detected_title {
Text text_csd_title {
{ 0, 3 * 16, (8 * 8), 16 },
"Detected",
"CSD",
};
Text text_detected_value {
{ 240 - (detected_characters * 8), 3 * 16, (detected_characters * 8), 16 },
Text text_csd_value_3 {
{ 240 - ((8 + 1 + 8) * 8), 3 * 16, (8 * 8), 16 },
"",
};
Text text_csd_value_2 {
{ 240 - (8 * 8), 3 * 16, (8 * 8), 16 },
"",
};
Text text_csd_value_1 {
{ 240 - ((8 + 1 + 8) * 8), 4 * 16, (8 * 8), 16 },
"",
};
Text text_csd_value_0 {
{ 240 - (8 * 8), 4 * 16, (8 * 8), 16 },
"",
};
@ -73,30 +86,18 @@ private:
"",
};
static constexpr size_t card_mode_characters = 10;
static constexpr size_t card_type_characters = 13;
Text text_card_mode_title {
Text text_card_type_title {
{ 0, 6 * 16, (9 * 8), 16 },
"Card mode",
"Card type",
};
Text text_card_mode_value {
{ 240 - (card_mode_characters * 8), 6 * 16, (card_mode_characters * 8), 16 },
Text text_card_type_value {
{ 240 - (card_type_characters * 8), 6 * 16, (card_type_characters * 8), 16 },
"",
};
// static constexpr size_t csd_characters = 10;
// Text text_csd_title {
// { 0, 7 * 16, (3 * 8), 16 },
// "CSD",
// };
// Text text_csd_value {
// { 240 - (csd_characters * 8), 7 * 16, (csd_characters * 8), 16 },
// "",
// };
static constexpr size_t block_size_characters = 5;
Text text_block_size_title {

View File

@ -20,16 +20,15 @@
*/
#include "ui_setup.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "portapack_persistent_memory.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
#include "portapack.hpp"
using portapack::receiver_model;
using namespace portapack;
#include "cpld_update.hpp"
namespace ui {
@ -170,15 +169,26 @@ void AntennaBiasSetupView::focus() {
button_done.focus();
}
SetTouchCalibView::SetTouchCalibView(NavigationView& nav) {
add_children({{
AboutView::AboutView(NavigationView& nav) {
add_children({ {
&text_title,
&text_debugx,
&text_debugy,
&button_ok
}});
&text_firmware,
&text_cpld_hackrf,
&text_cpld_hackrf_status,
&button_ok,
} });
button_ok.on_select = [&nav](Button&){ nav.pop(); };
if( cpld_hackrf_verify_eeprom() ) {
text_cpld_hackrf_status.set(" OK");
} else {
text_cpld_hackrf_status.set("BAD");
}
}
void AboutView::focus() {
button_ok.focus();
}
void SetTouchCalibView::focus() {

View File

@ -25,7 +25,6 @@
#include "ui_widget.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "ff.h"
#include <cstdint>
@ -247,6 +246,39 @@ private:
};
};
class AboutView : public View {
public:
AboutView(NavigationView& nav);
void focus() override;
private:
Text text_title {
{ 100, 96, 40, 16 },
"About",
};
Text text_firmware {
{ 0, 128, 240, 16 },
"Git Commit Hash " GIT_REVISION,
};
Text text_cpld_hackrf {
{ 0, 144, 11*8, 16 },
"HackRF CPLD",
};
Text text_cpld_hackrf_status {
{ 240 - 3*8, 144, 3*8, 16 },
"???"
};
Button button_ok {
{ 72, 192, 96, 24 },
"OK"
};
};
class SetUIView : public View {
public:
SetUIView(NavigationView& nav);

View File

@ -21,8 +21,6 @@
#include "ui_spectrum.hpp"
#include "event_m0.hpp"
#include "spectrum_color_lut.hpp"
#include "portapack.hpp"
@ -236,31 +234,11 @@ WaterfallWidget::WaterfallWidget() {
}
void WaterfallWidget::on_show() {
EventDispatcher::message_map().register_handler(Message::ID::ChannelSpectrumConfig,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
this->fifo = message.fifo;
}
);
EventDispatcher::message_map().register_handler(Message::ID::DisplayFrameSync,
[this](const Message* const) {
if( this->fifo ) {
ChannelSpectrum channel_spectrum;
while( fifo->out(channel_spectrum) ) {
this->on_channel_spectrum(channel_spectrum);
}
}
}
);
baseband::spectrum_streaming_start();
}
void WaterfallWidget::on_hide() {
baseband::spectrum_streaming_stop();
EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync);
EventDispatcher::message_map().unregister_handler(Message::ID::ChannelSpectrumConfig);
}
void WaterfallWidget::set_parent_rect(const Rect new_parent_rect) {

View File

@ -25,6 +25,8 @@
#include "ui.hpp"
#include "ui_widget.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include <cstdint>
@ -86,6 +88,25 @@ private:
FrequencyScale frequency_scale;
ChannelSpectrumFIFO* fifo { nullptr };
MessageHandlerRegistration message_handler_spectrum_config {
Message::ID::ChannelSpectrumConfig,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
this->fifo = message.fifo;
}
};
MessageHandlerRegistration message_handler_frame_sync {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
if( this->fifo ) {
ChannelSpectrum channel_spectrum;
while( fifo->out(channel_spectrum) ) {
this->on_channel_spectrum(channel_spectrum);
}
}
}
};
void on_channel_spectrum(const ChannelSpectrum& spectrum);
};

View File

@ -0,0 +1,366 @@
#
# Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
#
# This file is part of PortaPack.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
##############################################################################
# Build global options
# NOTE: Can be overridden externally.
#
enable_language(C CXX ASM)
project(baseband_shared)
# Compiler options here.
set(USE_OPT "-O3 -falign-functions=16 -fno-math-errno --specs=nano.specs")
# C specific options here (added to USE_OPT).
set(USE_COPT "-std=gnu99")
# C++ specific options here (added to USE_OPT).
set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions")
# Enable this if you want the linker to remove unused code and data
set(USE_LINK_GC yes)
# Linker extra options here.
set(USE_LDOPT)
# Enable this if you want link time optimizations (LTO)
set(USE_LTO no)
# If enabled, this option allows to compile the application in THUMB mode.
set(USE_THUMB yes)
# Enable this if you want to see the full log while compiling.
set(USE_VERBOSE_COMPILE no)
#
# Build global options
##############################################################################
##############################################################################
# Architecture or project specific options
#
# Enables the use of FPU on Cortex-M4 (no, softfp, hard).
set(USE_FPU hard)
#
# Architecture or project specific options
##############################################################################
##############################################################################
# Project, sources and paths
#
# Imported source files and paths
include(${CHIBIOS_PORTAPACK}/boards/GSG_HACKRF_ONE/board.cmake)
include(${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx_M4/platform.cmake)
include(${CHIBIOS}/os/hal/hal.cmake)
include(${CHIBIOS_PORTAPACK}/os/ports/GCC/ARMCMx/LPC43xx_M4/port.cmake)
include(${CHIBIOS}/os/kernel/kernel.cmake)
include(${CHIBIOS}/test/test.cmake)
# Define linker script file here
set(LDSCRIPT ${PORTLD}/LPC43xx_M4.ld)
# C sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
set(CSRC
${PORTSRC}
${KERNSRC}
${TESTSRC}
${HALSRC}
${PLATFORMSRC}
${BOARDSRC}
)
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
set(CPPSRC
baseband.cpp
${COMMON}/message_queue.cpp
${COMMON}/event.cpp
event_m4.cpp
${COMMON}/thread_wait.cpp
${COMMON}/gpdma.cpp
baseband_dma.cpp
${COMMON}/baseband_sgpio.cpp
${COMMON}/portapack_shared_memory.cpp
baseband_thread.cpp
baseband_processor.cpp
baseband_stats_collector.cpp
dsp_decimate.cpp
dsp_demodulate.cpp
matched_filter.cpp
spectrum_collector.cpp
stream_input.cpp
dsp_squelch.cpp
clock_recovery.cpp
packet_builder.cpp
${COMMON}/dsp_fft.cpp
${COMMON}/dsp_fir_taps.cpp
${COMMON}/dsp_iir.cpp
fxpt_atan2.cpp
rssi.cpp
rssi_dma.cpp
rssi_thread.cpp
audio_compressor.cpp
audio_output.cpp
audio_dma.cpp
audio_stats_collector.cpp
${COMMON}/utility.cpp
${COMMON}/chibios_cpp.cpp
${COMMON}/debug.cpp
${COMMON}/gcc.cpp
)
# C sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
set(ACSRC)
# C++ sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
set(ACPPSRC)
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
set(TCSRC)
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
set(TCPPSRC)
# List ASM source files here
set(ASMSRC ${PORTASM})
set(INCDIR ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC}
${HALINC} ${PLATFORMINC} ${BOARDINC}
${CHIBIOS}/os/various
)
#
# Project, sources and paths
##############################################################################
##############################################################################
# Compiler settings
#
set(MCU cortex-m4)
# ARM-specific options here
set(AOPT)
# THUMB-specific options here
set(TOPT "-mthumb -DTHUMB")
# Define C warning options here
set(CWARN "-Wall -Wextra -Wstrict-prototypes")
# Define C++ warning options here
set(CPPWARN "-Wall -Wextra")
#
# Compiler settings
##############################################################################
##############################################################################
# Start of default section
#
# List all default C defines here, like -D_DEBUG=1
# TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
# NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
set(DDEFS -DLPC43XX -DLPC43XX_M4 -D__NEWLIB__ -DHACKRF_ONE -DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 -DGIT_REVISION=\"${GIT_REVISION}\")
# List all default ASM defines here, like -D_DEBUG=1
set(DADEFS)
# List all default directories to look for include files here
set(DINCDIR)
# List the default directory to look for the libraries here
set(DLIBDIR)
# List all default libraries here
set(DLIBS)
#
# End of default section
##############################################################################
##############################################################################
# Start of user section
#
# List all user C define here, like -D_DEBUG=1
set(UDEFS)
# Define ASM defines here
set(UADEFS)
# List all user directories here
set(UINCDIR)
# List the user directory to look for the libraries here
set(ULIBDIR)
# List all user libraries here
set(ULIBS)
#
# End of user defines
##############################################################################
set(RULESPATH ${CHIBIOS}/os/ports/GCC/ARMCMx)
include(${RULESPATH}/rules.cmake)
#######################################################################
add_library(${PROJECT_NAME} OBJECT ${CSRC} ${CPPSRC} ${ASMSRC})
include_directories(. ${INCDIR})
#######################################################################
set(BASEBAND_IMAGES)
macro(DeclareTargets chunk_tag name)
project("baseband_${name}")
include(${RULESPATH}/rules.cmake)
add_executable(${PROJECT_NAME}.elf $<TARGET_OBJECTS:baseband_shared> ${MODE_CPPSRC})
set_target_properties(${PROJECT_NAME}.elf PROPERTIES LINK_DEPENDS ${LDSCRIPT})
add_definitions(${DEFS})
include_directories(. ${INCDIR})
link_directories(${LLIBDIR})
target_link_libraries(${PROJECT_NAME}.elf ${LIBS})
add_custom_command(
OUTPUT ${PROJECT_NAME}.bin ${PROJECT_NAME}.img
COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin
COMMAND ${MAKE_IMAGE_CHUNK} ${PROJECT_NAME}.bin ${chunk_tag} ${PROJECT_NAME}.img
DEPENDS ${PROJECT_NAME}.elf ${MAKE_IMAGE_CHUNK}
VERBATIM
)
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} ${PROJECT_NAME}.img)
endmacro()
### AIS
set(MODE_CPPSRC
proc_ais.cpp
)
DeclareTargets(PAIS ais)
### AM Audio
set(MODE_CPPSRC
proc_am_audio.cpp
)
DeclareTargets(PAMA am_audio)
### Capture
set(MODE_CPPSRC
proc_capture.cpp
)
DeclareTargets(PCAP capture)
### ERT
set(MODE_CPPSRC
proc_ert.cpp
)
DeclareTargets(PERT ert)
### NFM Audio
set(MODE_CPPSRC
proc_nfm_audio.cpp
)
DeclareTargets(PNFM nfm_audio)
### TPMS
set(MODE_CPPSRC
proc_tpms.cpp
)
DeclareTargets(PTPM tpms)
### WFM Audio
set(MODE_CPPSRC
proc_wfm_audio.cpp
)
DeclareTargets(PWFM wfm_audio)
### Wideband Spectrum
set(MODE_CPPSRC
proc_wideband_spectrum.cpp
)
DeclareTargets(PSPE wideband_spectrum)
### HackRF "factory" firmware
add_custom_command(
OUTPUT hackrf.bin hackrf.img
COMMAND ${STRIP_DFU} ${HACKRF_FIRMWARE_IMAGE} hackrf.bin
COMMAND ${MAKE_IMAGE_CHUNK} hackrf.bin HRF1 hackrf.img
DEPENDS ${HACKRF_FIRMWARE_IMAGE} ${STRIP_DFU} ${MAKE_IMAGE_CHUNK}
VERBATIM
)
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} hackrf.img)
### Terminator image
add_custom_command(
OUTPUT terminator.img
COMMAND ${MAKE_IMAGE_CHUNK} terminator.img
DEPENDS ${MAKE_IMAGE_CHUNK}
VERBATIM
)
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} terminator.img)
#######################################################################
project(baseband)
add_custom_command(
OUTPUT ${PROJECT_NAME}.img
COMMAND cat ${BASEBAND_IMAGES} > ${PROJECT_NAME}.img
DEPENDS ${BASEBAND_IMAGES}
VERBATIM
)
add_custom_target(
${PROJECT_NAME}
DEPENDS ${PROJECT_NAME}.img
)

View File

@ -28,27 +28,24 @@
#include "gpdma.hpp"
#include "event_m4.hpp"
#include "touch_dma.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "baseband_processor.hpp"
#include "message_queue.hpp"
#include "utility.hpp"
#include "debug.hpp"
#include "audio_dma.hpp"
#include "gcc.hpp"
static void init() {
audio::dma::init();
audio::dma::configure();
audio::dma::enable();
#include <cstdint>
#include <cstddef>
#include <array>
LPC_CREG->DMAMUX = portapack::gpdma_mux;
gpdma::controller.enable();
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));
}
static void halt() {
port_disable();
while(true) {
port_wait_for_interrupt();
}
}
extern "C" {
@ -67,32 +64,12 @@ void __late_init(void) {
* require the heap.
*/
chSysInit();
/* Baseband initialization */
init();
}
}
static void init() {
audio::dma::init();
audio::dma::configure();
audio::dma::enable();
LPC_CREG->DMAMUX = portapack::gpdma_mux;
gpdma::controller.enable();
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));
touch::dma::init();
touch::dma::allocate();
touch::dma::enable();
}
static void halt() {
port_disable();
while(true) {
port_wait_for_interrupt();
}
}
static void shutdown() {
void _default_exit(void) {
// TODO: Is this complete?
nvicDisableVector(DMA_IRQn);
@ -104,18 +81,9 @@ static void shutdown() {
ShutdownMessage shutdown_message;
shared_memory.application_queue.push(shutdown_message);
shared_memory.baseband_message = nullptr;
halt();
}
int main(void) {
init();
/* TODO: Ensure DMAs are configured to point at first LLI in chain. */
EventDispatcher event_dispatcher;
event_dispatcher.run();
shutdown();
return 0;
}

View File

@ -115,9 +115,16 @@ static void dma_error() {
void init() {
gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error);
// LPC_GPDMA->SYNC |= (1 << gpdma_src_peripheral);
// LPC_GPDMA->SYNC |= (1 << gpdma_dest_peripheral);
#if defined(PORTAPACK_BASEBAND_DMA_NO_SYNC)
/* Disable synchronization logic to improve(?) DMA response time.
* SGPIO (peripheral) must be on same clock as GPDMA peripheral.
* SGPIO runs from BASE_PERIPH_CLK, which is set to PLL1 in normal
* operation, same as the M4 and M0 cores. Memory, of course, is
* running from the same clock as the cores.
*/
LPC_GPDMA->SYNC |= (1 << gpdma_src_peripheral);
LPC_GPDMA->SYNC |= (1 << gpdma_dest_peripheral);
#endif
}
void configure(

View File

@ -31,8 +31,6 @@
namespace baseband {
namespace dma {
using Handler = void (*)();
void init();
void configure(
baseband::sample_t* const buffer_base,

View File

@ -31,15 +31,14 @@
#include "rssi.hpp"
#include "i2s.hpp"
#include "proc_am_audio.hpp"
/*#include "proc_am_audio.hpp"
#include "proc_nfm_audio.hpp"
#include "proc_wfm_audio.hpp"
#include "proc_ais.hpp"
#include "proc_wideband_spectrum.hpp"
#include "proc_closecall.hpp"
#include "proc_tpms.hpp"
#include "proc_ert.hpp"
#include "proc_capture.hpp"
#include "proc_capture.hpp"*/
#include "portapack_shared_memory.hpp"
@ -49,38 +48,25 @@ static baseband::SGPIO baseband_sgpio;
WORKING_AREA(baseband_thread_wa, 4096);
Thread* BasebandThread::start(const tprio_t priority) {
return chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa),
Thread* BasebandThread::thread = nullptr;
BasebandThread::BasebandThread(
uint32_t sampling_rate,
BasebandProcessor* const baseband_processor,
const tprio_t priority
) : baseband_processor { baseband_processor },
sampling_rate { sampling_rate }
{
thread = chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa),
priority, ThreadBase::fn,
this
);
}
void BasebandThread::set_configuration(const BasebandConfiguration& new_configuration) {
if( new_configuration.mode != baseband_configuration.mode ) {
disable();
// TODO: Timing problem around disabling DMA and nulling and deleting old processor
auto old_p = baseband_processor;
baseband_processor = nullptr;
delete old_p;
baseband_processor = create_processor(new_configuration.mode);
enable();
}
baseband_configuration = new_configuration;
}
void BasebandThread::on_message(const Message* const message) {
if( message->id == Message::ID::BasebandConfiguration ) {
set_configuration(reinterpret_cast<const BasebandConfigurationMessage*>(message)->configuration);
} else {
if( baseband_processor ) {
baseband_processor->on_message(message);
}
}
BasebandThread::~BasebandThread() {
chThdTerminate(thread);
chThdWait(thread);
thread = nullptr;
}
void BasebandThread::run() {
@ -94,35 +80,29 @@ void BasebandThread::run() {
);
//baseband::dma::allocate(4, 2048);
BasebandStatsCollector stats {
chSysGetIdleThread(),
thread_main,
thread_rssi,
chThdSelf()
};
baseband_sgpio.configure(direction());
baseband::dma::enable(direction());
baseband_sgpio.streaming_enable();
while(true) {
while( !chThdShouldTerminate() ) {
// TODO: Place correct sampling rate into buffer returned here:
const auto buffer_tmp = baseband::dma::wait_for_rx_buffer();
if( buffer_tmp ) {
buffer_c8_t buffer {
buffer_tmp.p, buffer_tmp.count, baseband_configuration.sampling_rate
buffer_tmp.p, buffer_tmp.count, sampling_rate
};
if( baseband_processor ) {
baseband_processor->execute(buffer);
}
stats.process(buffer,
[](const BasebandStatistics& statistics) {
const BasebandStatisticsMessage message { statistics };
shared_memory.application_queue.push(message);
}
);
}
}
}
i2s::i2s0::tx_mute();
baseband::dma::disable();
baseband_sgpio.streaming_disable();
}
/*
BasebandProcessor* BasebandThread::create_processor(const int32_t mode) {
switch(mode) {
case 0: return new NarrowbandAMAudio();
@ -133,7 +113,6 @@ BasebandProcessor* BasebandThread::create_processor(const int32_t mode) {
case 5: return new TPMSProcessor();
case 6: return new ERTProcessor();
case 7: return new CaptureProcessor();
case 10: return new CloseCallProcessor();
default: return nullptr;
}
}
@ -157,3 +136,4 @@ void BasebandThread::enable() {
baseband_sgpio.streaming_enable();
}
}
*/

View File

@ -30,9 +30,12 @@
class BasebandThread : public ThreadBase {
public:
Thread* start(const tprio_t priority);
void on_message(const Message* const message);
BasebandThread(
uint32_t sampling_rate,
BasebandProcessor* const baseband_processor,
const tprio_t priority
);
~BasebandThread();
// This getter should die, it's just here to leak information to code that
// isn't in the right place to begin with.
@ -40,24 +43,13 @@ public:
return baseband::Direction::Receive;
}
void wait_for_switch(void);
Thread* thread_main { nullptr };
Thread* thread_rssi { nullptr };
private:
BasebandProcessor* baseband_processor { nullptr };
static Thread* thread;
BasebandConfiguration baseband_configuration;
BasebandProcessor* baseband_processor { nullptr };
uint32_t sampling_rate;
void run() override;
BasebandProcessor* create_processor(const int32_t mode);
void disable();
void enable();
void set_configuration(const BasebandConfiguration& new_configuration);
};
#endif/*__BASEBAND_THREAD_H__*/

View File

@ -51,13 +51,16 @@ CH_IRQ_HANDLER(MAPP_IRQHandler) {
Thread* EventDispatcher::thread_event_loop = nullptr;
EventDispatcher::EventDispatcher(
std::unique_ptr<BasebandProcessor> baseband_processor
) : baseband_processor { std::move(baseband_processor) }
{
}
void EventDispatcher::run() {
thread_event_loop = chThdSelf();
lpc43xx::creg::m0apptxevent::enable();
baseband_thread.thread_main = chThdSelf();
baseband_thread.thread_rssi = rssi_thread.start(NORMALPRIO + 10);
baseband_thread.start(NORMALPRIO + 20);
lpc43xx::creg::m0apptxevent::enable();
while(is_running) {
const auto events = wait();
@ -86,9 +89,10 @@ void EventDispatcher::dispatch(const eventmask_t events) {
}
void EventDispatcher::handle_baseband_queue() {
shared_memory.baseband_queue.handle([this](Message* const message) {
this->on_message(message);
});
const auto message = shared_memory.baseband_message;
if( message ) {
on_message(message);
}
}
void EventDispatcher::on_message(const Message* const message) {
@ -99,6 +103,7 @@ void EventDispatcher::on_message(const Message* const message) {
default:
on_message_default(message);
shared_memory.baseband_message = nullptr;
break;
}
}
@ -108,10 +113,10 @@ void EventDispatcher::on_message_shutdown(const ShutdownMessage&) {
}
void EventDispatcher::on_message_default(const Message* const message) {
baseband_thread.on_message(message);
baseband_processor->on_message(message);
}
void EventDispatcher::handle_spectrum() {
const UpdateSpectrumMessage message;
baseband_thread.on_message(&message);
baseband_processor->on_message(&message);
}

View File

@ -36,26 +36,23 @@ constexpr auto EVT_MASK_SPECTRUM = EVENT_MASK(1);
class EventDispatcher {
public:
EventDispatcher(std::unique_ptr<BasebandProcessor> baseband_processor);
void run();
void request_stop();
static inline void events_flag(const eventmask_t events) {
if( thread_event_loop ) {
chEvtSignal(thread_event_loop, events);
}
chEvtSignal(thread_event_loop, events);
}
static inline void events_flag_isr(const eventmask_t events) {
if( thread_event_loop ) {
chEvtSignalI(thread_event_loop, events);
}
chEvtSignalI(thread_event_loop, events);
}
private:
static Thread* thread_event_loop;
BasebandThread baseband_thread;
RSSIThread rssi_thread;
std::unique_ptr<BasebandProcessor> baseband_processor;
bool is_running = true;

View File

@ -70,6 +70,7 @@ public:
constexpr OOKClockRecovery(
const float samples_per_symbol
) : symbol_phase_inc_nominal { static_cast<uint32_t>(std::round((1ULL << 32) / samples_per_symbol)) },
symbol_phase_inc_k { symbol_phase_inc_nominal * (2.0f / 8.0f) / samples_per_symbol },
phase_detector { samples_per_symbol },
phase_accumulator { symbol_phase_inc_nominal }
{
@ -79,13 +80,14 @@ public:
void operator()(const uint32_t slicer_history, SymbolHandler symbol_handler) {
if( phase_accumulator() ) {
const auto detector_result = phase_detector(slicer_history);
phase_accumulator.set_inc(symbol_phase_inc_nominal + detector_result.error * (symbol_phase_inc_nominal >> 3));
phase_accumulator.set_inc(symbol_phase_inc_nominal + detector_result.error * symbol_phase_inc_k);
symbol_handler(detector_result.symbol);
}
}
private:
const uint32_t symbol_phase_inc_nominal;
const uint32_t symbol_phase_inc_k;
PhaseDetectorEarlyLateGate phase_detector;
PhaseAccumulator phase_accumulator;
};

View File

@ -39,30 +39,32 @@ public:
};
constexpr PhaseDetectorEarlyLateGate(
const float samples_per_symbol
) : late_mask { (1U << static_cast<size_t>(std::ceil(samples_per_symbol / 2))) - 1 },
early_mask { late_mask << static_cast<size_t>(std::floor(samples_per_symbol / 2)) },
sample_bit { static_cast<size_t>(std::floor(samples_per_symbol / 2)) }
const size_t samples_per_symbol
) : sample_threshold { samples_per_symbol / 2 },
late_mask { (1UL << sample_threshold) - 1UL },
early_mask { late_mask << sample_threshold }
{
}
result_t operator()(const history_t symbol_history) const {
static_assert(sizeof(history_t) == sizeof(unsigned long), "popcountl size mismatch");
// history = ...0111, early
// history = ...1110, late
const symbol_t symbol = (symbol_history >> sample_bit) & 1;
const int late_side = __builtin_popcount(symbol_history & late_mask);
const int early_side = __builtin_popcount(symbol_history & early_mask);
const int lateness = late_side - early_side;
const int direction = lateness; //std::min(std::max(lateness, -1), 1);
const error_t error = direction;
const size_t late_side = __builtin_popcountl(symbol_history & late_mask);
const size_t early_side = __builtin_popcountl(symbol_history & early_mask);
const size_t total_count = late_side + early_side;
const auto lateness = static_cast<int>(late_side) - static_cast<int>(early_side);
const symbol_t symbol = (total_count >= sample_threshold);
const error_t error = symbol ? -lateness : lateness;
return { symbol, error };
}
private:
const size_t sample_threshold;
const history_t late_mask;
const history_t early_mask;
const size_t sample_bit;
};
#endif/*__PHASE_DETECTOR_HPP__*/

View File

@ -25,6 +25,8 @@
#include "dsp_fir_taps.hpp"
#include "event_m4.hpp"
AISProcessor::AISProcessor() {
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
decim_1.configure(taps_11k0_decim_1.taps, 131072);
@ -62,3 +64,9 @@ void AISProcessor::payload_handler(
const AISPacketMessage message { packet };
shared_memory.application_queue.push(message);
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<AISProcessor>() };
event_dispatcher.run();
return 0;
}

View File

@ -23,6 +23,7 @@
#define __PROC_AIS_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "channel_decimator.hpp"
#include "matched_filter.hpp"
@ -47,6 +48,10 @@ public:
void execute(const buffer_c8_t& buffer) override;
private:
static constexpr size_t baseband_fs = 2457600;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 };
std::array<complex16_t, 512> dst;
const buffer_c16_t dst_buffer {
dst.data(),

View File

@ -23,6 +23,8 @@
#include "audio_output.hpp"
#include "event_m4.hpp"
#include <array>
void NarrowbandAMAudio::execute(const buffer_c8_t& buffer) {
@ -105,3 +107,9 @@ void NarrowbandAMAudio::capture_config(const CaptureConfigMessage& message) {
audio_output.set_stream(nullptr);
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<NarrowbandAMAudio>() };
event_dispatcher.run();
return 0;
}

View File

@ -23,6 +23,8 @@
#define __PROC_AM_AUDIO_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "dsp_decimate.hpp"
#include "dsp_demodulate.hpp"
@ -44,6 +46,9 @@ private:
static constexpr size_t decim_2_decimation_factor = 4;
static constexpr size_t channel_filter_decimation_factor = 1;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst;
const buffer_c16_t dst_buffer {
dst.data(),

View File

@ -23,6 +23,8 @@
#include "dsp_fir_taps.hpp"
#include "event_m4.hpp"
#include "utility.hpp"
CaptureProcessor::CaptureProcessor() {
@ -90,3 +92,9 @@ void CaptureProcessor::capture_config(const CaptureConfigMessage& message) {
stream.reset();
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<CaptureProcessor>() };
event_dispatcher.run();
return 0;
}

View File

@ -23,6 +23,9 @@
#define __PROC_CAPTURE_HPP__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "dsp_decimate.hpp"
#include "spectrum_collector.hpp"
@ -45,6 +48,9 @@ private:
static constexpr size_t baseband_fs = 4000000;
static constexpr auto spectrum_rate_hz = 50.0f;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst;
const buffer_c16_t dst_buffer {
dst.data(),

View File

@ -23,6 +23,8 @@
#include "portapack_shared_memory.hpp"
#include "event_m4.hpp"
float ERTProcessor::abs(const complex8_t& v) {
// const int16_t r = v.real() - offset_i;
// const int16_t i = v.imag() - offset_q;
@ -101,3 +103,9 @@ void ERTProcessor::idm_handler(
const ERTPacketMessage message { ert::Packet::Type::IDM, packet };
shared_memory.application_queue.push(message);
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<ERTProcessor>() };
event_dispatcher.run();
return 0;
}

View File

@ -23,6 +23,7 @@
#define __PROC_ERT_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "channel_decimator.hpp"
@ -61,6 +62,8 @@ private:
const size_t samples_per_symbol = channel_sampling_rate / symbol_rate;
const float clock_recovery_rate = symbol_rate * 2;
BasebandThread baseband_thread { baseband_sampling_rate, this, NORMALPRIO + 20 };
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery {
clock_recovery_rate, symbol_rate, { 1.0f / 18.0f },
[this](const float symbol) { this->consume_symbol(symbol); }

View File

@ -23,6 +23,8 @@
#include "audio_output.hpp"
#include "event_m4.hpp"
#include <cstdint>
#include <cstddef>
@ -93,3 +95,9 @@ void NarrowbandFMAudio::capture_config(const CaptureConfigMessage& message) {
audio_output.set_stream(nullptr);
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<NarrowbandFMAudio>() };
event_dispatcher.run();
return 0;
}

View File

@ -23,6 +23,8 @@
#define __PROC_NFM_AUDIO_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "dsp_decimate.hpp"
#include "dsp_demodulate.hpp"
@ -41,6 +43,9 @@ public:
private:
static constexpr size_t baseband_fs = 3072000;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst;
const buffer_c16_t dst_buffer {
dst.data(),

View File

@ -23,6 +23,8 @@
#include "dsp_fir_taps.hpp"
#include "event_m4.hpp"
TPMSProcessor::TPMSProcessor() {
decim_0.configure(taps_200k_decim_0.taps, 33554432);
decim_1.configure(taps_200k_decim_1.taps, 131072);
@ -32,41 +34,32 @@ void TPMSProcessor::execute(const buffer_c8_t& buffer) {
/* 2.4576MHz, 2048 samples */
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto decimator_out = decim_1_out;
const auto decimator_out = decim_1.execute(decim_0_out, dst_buffer);
/* 307.2kHz, 256 samples */
feed_channel_stats(decimator_out);
for(size_t i=0; i<decimator_out.count; i++) {
if( mf.execute_once(decimator_out.p[i]) ) {
clock_recovery(mf.get_output());
if( mf_38k4_1t_19k2.execute_once(decimator_out.p[i]) ) {
clock_recovery_fsk_19k2(mf_38k4_1t_19k2.get_output());
}
}
for(size_t i=0; i<decim_1_out.count; i+=channel_decimation) {
const auto sliced = ook_slicer_5sps(decim_1_out.p[i]);
for(size_t i=0; i<decimator_out.count; i+=channel_decimation) {
const auto sliced = ook_slicer_5sps(decimator_out.p[i]);
slicer_history = (slicer_history << 1) | sliced;
ook_clock_recovery_subaru(slicer_history, [this](const bool symbol) {
this->packet_builder_ook_subaru.execute(symbol);
clock_recovery_ook_8k192(slicer_history, [this](const bool symbol) {
this->packet_builder_ook_8k192_schrader.execute(symbol);
});
ook_clock_recovery_gmc(slicer_history, [this](const bool symbol) {
this->packet_builder_ook_gmc.execute(symbol);
clock_recovery_ook_8k4(slicer_history, [this](const bool symbol) {
this->packet_builder_ook_8k4_schrader.execute(symbol);
});
}
}
void TPMSProcessor::consume_symbol(
const float raw_symbol
) {
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
packet_builder.execute(sliced_symbol);
}
void TPMSProcessor::payload_handler(
const baseband::Packet& packet
) {
const TPMSPacketMessage message { tpms::SignalType::FLM, packet };
shared_memory.application_queue.push(message);
int main() {
EventDispatcher event_dispatcher { std::make_unique<TPMSProcessor>() };
event_dispatcher.run();
return 0;
}

View File

@ -23,6 +23,8 @@
#define __PROC_TPMS_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "channel_decimator.hpp"
#include "matched_filter.hpp"
@ -44,7 +46,7 @@
// Translate+rectangular filter
// sample=307.2k, deviation=38400, symbol=19200
// Length: 16 taps, 1 symbols, 2 cycles of sinusoid
constexpr std::array<std::complex<float>, 16> rect_taps_307k2_1t_p { {
constexpr std::array<std::complex<float>, 16> rect_taps_307k2_38k4_1t_19k2_p { {
{ 6.2500000000e-02f, 0.0000000000e+00f }, { 4.4194173824e-02f, 4.4194173824e-02f },
{ 0.0000000000e+00f, 6.2500000000e-02f }, { -4.4194173824e-02f, 4.4194173824e-02f },
{ -6.2500000000e-02f, 0.0000000000e+00f }, { -4.4194173824e-02f, -4.4194173824e-02f },
@ -62,6 +64,11 @@ public:
void execute(const buffer_c8_t& buffer) override;
private:
static constexpr size_t baseband_fs = 2457600;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst;
const buffer_c16_t dst_buffer {
dst.data(),
@ -71,55 +78,66 @@ private:
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0;
dsp::decimate::FIRC16xR16x16Decim2 decim_1;
dsp::matched_filter::MatchedFilter mf { rect_taps_307k2_1t_p, 8 };
dsp::matched_filter::MatchedFilter mf_38k4_1t_19k2 { rect_taps_307k2_38k4_1t_19k2_p, 8 };
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery {
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery_fsk_19k2 {
38400, 19200, { 0.0555f },
[this](const float symbol) { this->consume_symbol(symbol); }
[this](const float raw_symbol) {
const uint_fast8_t sliced_symbol = (raw_symbol >= 0.0f) ? 1 : 0;
this->packet_builder_fsk_19k2_schrader.execute(sliced_symbol);
}
};
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder {
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_fsk_19k2_schrader {
{ 0b010101010101010101010101010110, 30, 1 },
{ },
{ 256 },
{ 160 },
[this](const baseband::Packet& packet) {
this->payload_handler(packet);
const TPMSPacketMessage message { tpms::SignalType::FSK_19k2_Schrader, packet };
shared_memory.application_queue.push(message);
}
};
static constexpr float channel_rate_in = 307200.0f;
static constexpr size_t channel_decimation = 8;
static constexpr float channel_sample_rate = channel_rate_in / channel_decimation;
OOKSlicerMagSquaredInt ook_slicer_5sps { 5 };
OOKSlicerMagSquaredInt ook_slicer_5sps { channel_sample_rate / 8400 + 1};
uint32_t slicer_history { 0 };
OOKClockRecovery ook_clock_recovery_subaru {
OOKClockRecovery clock_recovery_ook_8k192 {
channel_sample_rate / 8192.0f
};
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_ook_subaru {
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_ook_8k192_schrader {
/* Preamble: 11*2, 01*14, 11, 10
* Payload: 37 Manchester-encoded bits
* Bit rate: 4096 Hz
*/
{ 0b010101010101010101011110, 24, 0 },
{ },
{ 80 },
{ 37 * 2 },
[](const baseband::Packet& packet) {
const TPMSPacketMessage message { tpms::SignalType::Subaru, packet };
const TPMSPacketMessage message { tpms::SignalType::OOK_8k192_Schrader, packet };
shared_memory.application_queue.push(message);
}
};
OOKClockRecovery ook_clock_recovery_gmc {
OOKClockRecovery clock_recovery_ook_8k4 {
channel_sample_rate / 8400.0f
};
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_ook_gmc {
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_ook_8k4_schrader {
/* Preamble: 01*40, 01, 10, 01, 01
* Payload: 76 Manchester-encoded bits
* Bit rate: 4200 Hz
*/
{ 0b01010101010101010101010101100101, 32, 0 },
{ },
{ 192 },
{ 76 * 2 },
[](const baseband::Packet& packet) {
const TPMSPacketMessage message { tpms::SignalType::GMC, packet };
const TPMSPacketMessage message { tpms::SignalType::OOK_8k4_Schrader, packet };
shared_memory.application_queue.push(message);
}
};
void consume_symbol(const float symbol);
void payload_handler(const baseband::Packet& packet);
};
#endif/*__PROC_TPMS_H__*/

View File

@ -23,6 +23,8 @@
#include "audio_output.hpp"
#include "event_m4.hpp"
#include <cstdint>
void WidebandFMAudio::execute(const buffer_c8_t& buffer) {
@ -122,3 +124,9 @@ void WidebandFMAudio::capture_config(const CaptureConfigMessage& message) {
audio_output.set_stream(nullptr);
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<WidebandFMAudio>() };
event_dispatcher.run();
return 0;
}

View File

@ -23,6 +23,8 @@
#define __PROC_WFM_AUDIO_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "dsp_decimate.hpp"
#include "dsp_demodulate.hpp"
@ -40,6 +42,9 @@ private:
static constexpr size_t baseband_fs = 3072000;
static constexpr auto spectrum_rate_hz = 50.0f;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst;
const buffer_c16_t dst_buffer {
dst.data(),

View File

@ -72,3 +72,9 @@ void WidebandSpectrum::on_message(const Message* const message) {
break;
}
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<WidebandSpectrum>() };
event_dispatcher.run();
return 0;
}

View File

@ -23,6 +23,9 @@
#define __PROC_WIDEBAND_SPECTRUM_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "spectrum_collector.hpp"
#include "message.hpp"
@ -38,6 +41,11 @@ public:
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 20000000;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20 };
RSSIThread rssi_thread { NORMALPRIO + 10 };
SpectrumCollector channel_spectrum;
std::array<complex16_t, 256> spectrum;

View File

@ -30,8 +30,6 @@ namespace rf {
namespace rssi {
namespace dma {
using Handler = void (*)();
void init();
void allocate(size_t buffer_count, size_t items_per_buffer);

View File

@ -30,20 +30,30 @@
WORKING_AREA(rssi_thread_wa, 128);
Thread* RSSIThread::start(const tprio_t priority) {
return chThdCreateStatic(rssi_thread_wa, sizeof(rssi_thread_wa),
Thread* RSSIThread::thread = nullptr;
RSSIThread::RSSIThread(const tprio_t priority) {
thread = chThdCreateStatic(rssi_thread_wa, sizeof(rssi_thread_wa),
priority, ThreadBase::fn,
this
);
}
RSSIThread::~RSSIThread() {
chThdTerminate(thread);
chThdWait(thread);
thread = nullptr;
}
void RSSIThread::run() {
rf::rssi::init();
rf::rssi::dma::allocate(4, 400);
RSSIStatisticsCollector stats;
while(true) {
rf::rssi::start();
while( !chThdShouldTerminate() ) {
// TODO: Place correct sampling rate into buffer returned here:
const auto buffer_tmp = rf::rssi::dma::wait_for_buffer();
const rf::rssi::buffer_t buffer {
@ -59,5 +69,6 @@ void RSSIThread::run() {
);
}
rf::rssi::stop();
rf::rssi::dma::free();
}

View File

@ -30,11 +30,14 @@
class RSSIThread : public ThreadBase {
public:
Thread* start(const tprio_t priority);
RSSIThread(const tprio_t priority);
~RSSIThread();
private:
void run() override;
static Thread* thread;
const uint32_t sampling_rate { 400000 };
};

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "stream_input.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
StreamInput::StreamInput(CaptureConfig* const config) :
fifo_buffers_empty { buffers_empty.data(), buffer_count_max_log2 },
fifo_buffers_full { buffers_full.data(), buffer_count_max_log2 },
config { config },
data { std::make_unique<uint8_t[]>(config->write_size * config->buffer_count) }
{
config->fifo_buffers_empty = &fifo_buffers_empty;
config->fifo_buffers_full = &fifo_buffers_full;
for(size_t i=0; i<config->buffer_count; i++) {
buffers[i] = { &(data.get()[i * config->write_size]), config->write_size };
fifo_buffers_empty.in(&buffers[i]);
}
}
size_t StreamInput::write(const void* const data, const size_t length) {
const uint8_t* p = static_cast<const uint8_t*>(data);
size_t written = 0;
while( written < length ) {
if( !active_buffer ) {
// We need an empty buffer...
if( !fifo_buffers_empty.out(active_buffer) ) {
// ...but none are available. Samples were dropped.
break;
}
}
const auto remaining = length - written;
written += active_buffer->write(&p[written], remaining);
if( active_buffer->is_full() ) {
if( !fifo_buffers_full.in(active_buffer) ) {
// FIFO is fuil of buffers, there's no place for this one.
// Bail out of the loop, and try submitting the buffer in the
// next pass.
// This should never happen if the number of buffers is less
// than the capacity of the FIFO.
break;
}
active_buffer = nullptr;
creg::m4txevent::assert();
}
}
config->baseband_bytes_received += length;
config->baseband_bytes_dropped += (length - written);
return written;
}

View File

@ -25,46 +25,29 @@
#include "message.hpp"
#include "fifo.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
#include <cstdint>
#include <cstddef>
#include <array>
#include <memory>
class StreamInput {
public:
StreamInput(CaptureConfig* const config) :
config { config },
K { config->write_size_log2 + config->buffer_count_log2 },
event_bytes_mask { (1UL << config->write_size_log2) - 1 },
data { std::make_unique<uint8_t[]>(1UL << K) },
fifo { data.get(), K }
{
config->fifo = &fifo;
}
StreamInput(CaptureConfig* const config);
size_t write(const void* const data, const size_t length) {
const auto written = fifo.in(reinterpret_cast<const uint8_t*>(data), length);
const auto last_bytes_written = bytes_written;
bytes_written += written;
if( (bytes_written & event_bytes_mask) < (last_bytes_written & event_bytes_mask) ) {
creg::m4txevent::assert();
}
config->baseband_bytes_received += length;
config->baseband_bytes_dropped = config->baseband_bytes_received - bytes_written;
return written;
}
size_t write(const void* const data, const size_t length);
private:
CaptureConfig* const config;
const size_t K;
const uint64_t event_bytes_mask;
uint64_t bytes_written = 0;
static constexpr size_t buffer_count_max_log2 = 3;
static constexpr size_t buffer_count_max = 1U << buffer_count_max_log2;
FIFO<StreamBuffer*> fifo_buffers_empty;
FIFO<StreamBuffer*> fifo_buffers_full;
std::array<StreamBuffer, buffer_count_max> buffers;
std::array<StreamBuffer*, buffer_count_max> buffers_empty;
std::array<StreamBuffer*, buffer_count_max> buffers_full;
StreamBuffer* active_buffer { nullptr };
CaptureConfig* const config { nullptr };
std::unique_ptr<uint8_t[]> data;
FIFO<uint8_t> fifo;
};
#endif/*__STREAM_INPUT_H__*/

View File

@ -32,8 +32,6 @@ namespace dma {
using sample_t = uint32_t;
using buffer_t = buffer_t<sample_t>;
using Handler = void (*)();
void init();
void allocate();

View File

@ -1,4 +1,3 @@
#
# Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
#
@ -25,53 +24,33 @@
# NOTE: Can be overridden externally.
#
enable_language(C CXX ASM)
project(bootstrap)
# Compiler options here.
ifeq ($(USE_OPT),)
USE_OPT = -mthumb \
-O3 -ggdb3 \
-ffunction-sections \
-fdata-sections \
-fno-builtin \
-falign-functions=16 \
-fno-math-errno \
--specs=nano.specs
#-fomit-frame-pointer
endif
set(USE_OPT "-Os -falign-functions=16 -fno-math-errno --specs=nano.specs")
# C specific options here (added to USE_OPT).
ifeq ($(USE_COPT),)
USE_COPT = -std=gnu99
endif
set(USE_COPT "-std=gnu99")
# C++ specific options here (added to USE_OPT).
ifeq ($(USE_CPPOPT),)
USE_CPPOPT = -std=c++11 -fno-rtti -fno-exceptions
endif
set(USE_CPPOPT "-std=c++11 -fno-rtti -fno-exceptions")
# Enable this if you want the linker to remove unused code and data
ifeq ($(USE_LINK_GC),)
USE_LINK_GC = yes
endif
set(USE_LINK_GC yes)
# Linker extra options here.
ifeq ($(USE_LDOPT),)
USE_LDOPT =
endif
set(USE_LDOPT)
# Enable this if you want link time optimizations (LTO)
ifeq ($(USE_LTO),)
USE_LTO = no
endif
set(USE_LTO no)
# If enabled, this option allows to compile the application in THUMB mode.
ifeq ($(USE_THUMB),)
USE_THUMB = yes
endif
set(USE_THUMB yes)
# Enable this if you want to see the full log while compiling.
ifeq ($(USE_VERBOSE_COMPILE),)
USE_VERBOSE_COMPILE = no
endif
set(USE_VERBOSE_COMPILE no)
#
# Build global options
@ -82,9 +61,7 @@ endif
#
# Enables the use of FPU on Cortex-M4 (no, softfp, hard).
ifeq ($(USE_FPU),)
USE_FPU = hard
endif
set(USE_FPU no)
#
# Architecture or project specific options
@ -94,106 +71,48 @@ endif
# Project, sources and paths
#
# Define project name here
PROJECT = baseband
# Imported source files and paths
CHIBIOS = ../chibios
CHIBIOS_PORTAPACK = ../chibios-portapack
include $(CHIBIOS_PORTAPACK)/boards/GSG_HACKRF_ONE/board.mk
include $(CHIBIOS_PORTAPACK)/os/hal/platforms/LPC43xx_M4/platform.mk
include $(CHIBIOS)/os/hal/hal.mk
include $(CHIBIOS_PORTAPACK)/os/ports/GCC/ARMCMx/LPC43xx_M4/port.mk
include $(CHIBIOS)/os/kernel/kernel.mk
include $(CHIBIOS)/test/test.mk
# Define linker script file here
LDSCRIPT= $(PORTLD)/LPC43xx_M4.ld
set(LDSCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/m4.ld)
# C sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
CSRC = $(PORTSRC) \
$(KERNSRC) \
$(TESTSRC) \
$(HALSRC) \
$(PLATFORMSRC) \
$(BOARDSRC)
set(CSRC
bootstrap.c
)
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
CPPSRC = main.cpp \
message_queue.cpp \
event.cpp \
event_m4.cpp \
thread_wait.cpp \
gpdma.cpp \
baseband_dma.cpp \
baseband_sgpio.cpp \
portapack_shared_memory.cpp \
baseband_thread.cpp \
baseband_processor.cpp \
baseband_stats_collector.cpp \
dsp_decimate.cpp \
dsp_demodulate.cpp \
matched_filter.cpp \
proc_am_audio.cpp \
proc_nfm_audio.cpp \
spectrum_collector.cpp \
proc_wfm_audio.cpp \
proc_ais.cpp \
proc_wideband_spectrum.cpp \
proc_closecall.cpp \
proc_tpms.cpp \
proc_ert.cpp \
proc_capture.cpp \
dsp_squelch.cpp \
clock_recovery.cpp \
packet_builder.cpp \
dsp_fft.cpp \
dsp_fir_taps.cpp \
dsp_iir.cpp \
fxpt_atan2.cpp \
rssi.cpp \
rssi_dma.cpp \
rssi_thread.cpp \
audio_compressor.cpp \
audio_output.cpp \
audio_dma.cpp \
audio_stats_collector.cpp \
touch_dma.cpp \
../common/utility.cpp \
../common/chibios_cpp.cpp \
../common/debug.cpp \
../common/gcc.cpp
set(CPPSRC)
# C sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
ACSRC =
set(ACSRC)
# C++ sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
ACPPSRC =
set(ACPPSRC)
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
TCSRC =
set(TCSRC)
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
TCPPSRC =
set(TCPPSRC)
# List ASM source files here
ASMSRC = $(PORTASM)
set(ASMSRC startup_ARMCM4.S)
INCDIR = ../common $(PORTINC) $(KERNINC) $(TESTINC) \
$(HALINC) $(PLATFORMINC) $(BOARDINC) \
$(CHIBIOS)/os/various
set(INCDIR
${CHIBIOS}/os/ports/common/ARMCMx/CMSIS/include
${CHIBIOS}/os/ports/common/ARMCMx
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx_M4
)
#
# Project, sources and paths
@ -203,35 +122,19 @@ INCDIR = ../common $(PORTINC) $(KERNINC) $(TESTINC) \
# Compiler settings
#
MCU = cortex-m4
#TRGT = arm-elf-
TRGT = /usr/local/gcc-arm-none-eabi-5_2-2015q4/bin/arm-none-eabi-
CC = $(TRGT)gcc
CPPC = $(TRGT)g++
# Enable loading with g++ only if you need C++ runtime support.
# NOTE: You can use C++ even without C++ support if you are careful. C++
# runtime support makes code size explode.
#LD = $(TRGT)gcc
LD = $(TRGT)g++
CP = $(TRGT)objcopy
AS = $(TRGT)gcc -x assembler-with-cpp
OD = $(TRGT)objdump
SZ = $(TRGT)size
HEX = $(CP) -O ihex
BIN = $(CP) -O binary
set(MCU cortex-m4)
# ARM-specific options here
AOPT =
set(AOPT)
# THUMB-specific options here
TOPT = -mthumb -DTHUMB
set(TOPT "-mthumb -DTHUMB")
# Define C warning options here
CWARN = -Wall -Wextra -Wstrict-prototypes
set(CWARN "-Wall -Wextra -Wstrict-prototypes")
# Define C++ warning options here
CPPWARN = -Wall -Wextra
set(CPPWARN "-Wall -Wextra")
#
# Compiler settings
@ -244,21 +147,19 @@ CPPWARN = -Wall -Wextra
# List all default C defines here, like -D_DEBUG=1
# TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
# NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
DDEFS = -DLPC43XX -DLPC43XX_M4 -D__NEWLIB__ -DHACKRF_ONE \
-DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0 \
-DGIT_REVISION=\"$(GIT_REVISION)\"
set(DDEFS -DLPC43XX -DLPC43XX_M4 -D__START=main -DGIT_REVISION=\"${GIT_REVISION}\")
# List all default ASM defines here, like -D_DEBUG=1
DADEFS =
set(DADEFS)
# List all default directories to look for include files here
DINCDIR =
set(DINCDIR)
# List the default directory to look for the libraries here
DLIBDIR =
set(DLIBDIR)
# List all default libraries here
DLIBS =
set(DLIBS)
#
# End of default section
@ -269,23 +170,43 @@ DLIBS =
#
# List all user C define here, like -D_DEBUG=1
UDEFS =
set(UDEFS)
# Define ASM defines here
UADEFS =
set(UADEFS)
# List all user directories here
UINCDIR =
set(UINCDIR)
# List the user directory to look for the libraries here
ULIBDIR =
set(ULIBDIR)
# List all user libraries here
ULIBS =
set(ULIBS)
#
# End of user defines
##############################################################################
RULESPATH = $(CHIBIOS)/os/ports/GCC/ARMCMx
include $(RULESPATH)/rules.mk
set(RULESPATH ${CHIBIOS}/os/ports/GCC/ARMCMx)
include(${RULESPATH}/rules.cmake)
##############################################################################
add_executable(${PROJECT_NAME}.elf ${CSRC} ${CPPSRC} ${ASMSRC})
set_target_properties(${PROJECT_NAME}.elf PROPERTIES LINK_DEPENDS ${LDSCRIPT})
add_definitions(${DEFS})
include_directories(. ${INCDIR})
link_directories(${LLIBDIR})
target_link_libraries(${PROJECT_NAME}.elf ${LIBS})
add_custom_command(
OUTPUT ${PROJECT_NAME}.bin
COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin
DEPENDS ${PROJECT_NAME}.elf
)
add_custom_target(
bootstrap
DEPENDS ${PROJECT_NAME}.bin
)

View File

@ -1,65 +0,0 @@
#
# Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
#
# This file is part of PortaPack.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
TARGET=bootstrap
CC=arm-none-eabi-gcc
LD=arm-none-eabi-gcc
CP=arm-none-eabi-objcopy
OBJDUMP=arm-none-eabi-objdump
CHIBIOS_PORTAPACK=../chibios-portapack
CHIBIOS=../chibios
INCLUDE=-I $(CHIBIOS)/os/ports/common/ARMCMx/CMSIS/include \
-I $(CHIBIOS)/os/ports/common/ARMCMx \
-I $(CHIBIOS_PORTAPACK)/os/hal/platforms/LPC43xx \
-I $(CHIBIOS_PORTAPACK)/os/hal/platforms/LPC43xx_M4
MCPU=cortex-m4
CPUFLAGS=-mcpu=$(MCPU) -mthumb -mno-thumb-interwork -DTHUMB -DTHUMB_PRESENT -DTHUMB_NO_INTERWORKING
COPT=-std=gnu99 \
-Wall -Wextra -Wstrict-prototypes \
$(CPUFLAGS) \
-DLPC43XX -DLPC43XX_M4 \
-DGIT_REVISION=\"$(GIT_REVISION)\" \
-Os \
-ffunction-sections \
-fdata-sections \
-fno-builtin --specs=nano.specs
LDOPT=-nostartfiles \
$(CPUFLAGS) \
-D__START=main \
-Wl,-Map=$(TARGET).map,--cref,--no-warn-mismatch,--library-path=.,--script=m4.ld,--gc-sections
all: $(TARGET).elf
$(TARGET).lst: $(TARGET).elf
$(OBJDUMP) -S $(TARGET).elf >$(TARGET).lst
$(TARGET).elf: $(TARGET).o startup_ARMCM4.S
$(LD) $(LDOPT) $(LIB) -o $(TARGET).elf $(TARGET).o startup_ARMCM4.S
$(TARGET).o: $(TARGET).c
$(CC) $(COPT) $(INCLUDE) -c -o $(TARGET).o $(TARGET).c
clean:
rm -f $(TARGET).o $(TARGET).elf $(TARGET).bin $(TARGET).lst $(TARGET).map

View File

@ -0,0 +1,9 @@
# List of all the board related files.
set(BOARDSRC
${CHIBIOS_PORTAPACK}/boards/GSG_HACKRF_ONE/board.c
)
# Required include directories
set(BOARDINC
${CHIBIOS_PORTAPACK}/boards/GSG_HACKRF_ONE
)

View File

@ -200,8 +200,14 @@ static void sdio_cclk_set_400khz(void) {
}
static void sdio_cclk_set_fast(void) {
#if defined(PORTAPACK_FAST_SDIO)
/* 200MHz / (2 * 2) = 50MHz */
/* TODO: Adjust SCU pin configurations: pull-up/down, slew, glitch filter? */
sdio_cclk_set(2);
#else
/* 200MHz / (2 * 4) = 25MHz */
sdio_cclk_set(4);
#endif
}
static void sdio_width_set_1bit(void) {

View File

@ -0,0 +1,17 @@
# List of all the LPC43xx M0 platform files.
set(PLATFORMSRC
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx_M0/hal_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/gpt_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/i2c_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/pal_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/rtc_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/sdc_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/serial_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/spi_lld.c
)
# Required include directories
set(PLATFORMINC
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx_M0
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx
)

View File

@ -0,0 +1,17 @@
# List of all the LPC43xx M4 platform files.
set(PLATFORMSRC
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx_M4/hal_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/gpt_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/i2c_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/pal_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/rtc_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/sdc_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/serial_lld.c
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx/spi_lld.c
)
# Required include directories
set(PLATFORMINC
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx_M4
${CHIBIOS_PORTAPACK}/os/hal/platforms/LPC43xx
)

View File

@ -0,0 +1,21 @@
# List of the ChibiOS/RT Cortex-M0 LPC43xx port files.
set(PORTSRC
${CHIBIOS}/os/ports/GCC/ARMCMx/crt0.c
${CHIBIOS_PORTAPACK}/os/ports/GCC/ARMCMx/LPC43xx_M0/vectors.c
${CHIBIOS}/os/ports/GCC/ARMCMx/chcore.c
${CHIBIOS}/os/ports/GCC/ARMCMx/chcore_v6m.c
${CHIBIOS}/os/ports/common/ARMCMx/nvic.c
)
set(PORTASM)
set(PORTINC
${CHIBIOS}/os/ports/common/ARMCMx/CMSIS/include
${CHIBIOS}/os/ports/common/ARMCMx
${CHIBIOS}/os/ports/GCC/ARMCMx
${CHIBIOS_PORTAPACK}/os/ports/GCC/ARMCMx/LPC43xx_M0
)
set(PORTLD
${CHIBIOS_PORTAPACK}/os/ports/GCC/ARMCMx/LPC43xx_M0/ld
)

View File

@ -0,0 +1,21 @@
# List of the ChibiOS/RT Cortex-M4 LPC43xx port files.
set(PORTSRC
${CHIBIOS}/os/ports/GCC/ARMCMx/crt0.c
${CHIBIOS_PORTAPACK}/os/ports/GCC/ARMCMx/LPC43xx_M4/vectors.c
${CHIBIOS}/os/ports/GCC/ARMCMx/chcore.c
${CHIBIOS}/os/ports/GCC/ARMCMx/chcore_v7m.c
${CHIBIOS}/os/ports/common/ARMCMx/nvic.c
)
set(PORTASM)
set(PORTINC
${CHIBIOS}/os/ports/common/ARMCMx/CMSIS/include
${CHIBIOS}/os/ports/common/ARMCMx
${CHIBIOS}/os/ports/GCC/ARMCMx
${CHIBIOS_PORTAPACK}/os/ports/GCC/ARMCMx/LPC43xx_M4
)
set(PORTLD
${CHIBIOS_PORTAPACK}/os/ports/GCC/ARMCMx/LPC43xx_M4/ld
)

View File

@ -0,0 +1,10 @@
# FATFS files.
set(FATFSSRC
${CHIBIOS_PORTAPACK}/os/various/fatfs_bindings/fatfs_diskio.c
${CHIBIOS_PORTAPACK}/os/various/fatfs_bindings/fatfs_syscall.c
${CHIBIOS_PORTAPACK}/ext/fatfs/src/ff.c
)
set(FATFSINC
${CHIBIOS_PORTAPACK}/ext/fatfs/src
)

View File

@ -10,6 +10,8 @@
#include "ch.h"
#include "hal.h"
#include <string.h>
#include "diskio.h"
#if HAL_USE_MMC_SPI && HAL_USE_SDC
@ -212,6 +214,12 @@ DRESULT disk_ioctl (
case GET_SECTOR_SIZE:
*((WORD *)buff) = MMCSD_BLOCK_SIZE;
return RES_OK;
case MMC_GET_TYPE:
*((BYTE *)buff) = SDCD1.cardmode;
return RES_OK;
case MMC_GET_CSD:
memcpy(buff, &SDCD1.csd, sizeof(SDCD1.csd));
return RES_OK;
#if _USE_ERASE
case CTRL_ERASE_SECTOR:
mmcErase(&MMCD1, *((DWORD *)buff), *((DWORD *)buff + 1));
@ -232,7 +240,13 @@ DRESULT disk_ioctl (
*((WORD *)buff) = MMCSD_BLOCK_SIZE;
return RES_OK;
case GET_BLOCK_SIZE:
*((DWORD *)buff) = 256; /* 512b blocks in one erase block */
*((DWORD *)buff) = 1; /* Unknown, TODO: implement? */
return RES_OK;
case MMC_GET_TYPE:
*((BYTE *)buff) = SDCD1.cardmode;
return RES_OK;
case MMC_GET_CSD:
memcpy(buff, &SDCD1.csd, sizeof(SDCD1.csd));
return RES_OK;
#if _USE_ERASE
case CTRL_ERASE_SECTOR:

View File

@ -0,0 +1,29 @@
# List of all the ChibiOS/RT HAL files, there is no need to remove the files
# from this list, you can disable parts of the HAL by editing halconf.h.
set(HALSRC
${CHIBIOS}/os/hal/src/hal.c
${CHIBIOS}/os/hal/src/adc.c
${CHIBIOS}/os/hal/src/can.c
${CHIBIOS}/os/hal/src/ext.c
${CHIBIOS}/os/hal/src/gpt.c
${CHIBIOS}/os/hal/src/i2c.c
${CHIBIOS}/os/hal/src/icu.c
${CHIBIOS}/os/hal/src/mac.c
${CHIBIOS}/os/hal/src/mmc_spi.c
${CHIBIOS}/os/hal/src/mmcsd.c
${CHIBIOS}/os/hal/src/pal.c
${CHIBIOS}/os/hal/src/pwm.c
${CHIBIOS}/os/hal/src/rtc.c
${CHIBIOS}/os/hal/src/sdc.c
${CHIBIOS}/os/hal/src/serial.c
${CHIBIOS}/os/hal/src/serial_usb.c
${CHIBIOS}/os/hal/src/spi.c
${CHIBIOS}/os/hal/src/tm.c
${CHIBIOS}/os/hal/src/uart.c
${CHIBIOS}/os/hal/src/usb.c
)
# Required include directories
set(HALINC
${CHIBIOS}/os/hal/include
)

View File

@ -0,0 +1,27 @@
# List of all the ChibiOS/RT kernel files, there is no need to remove the files
# from this list, you can disable parts of the kernel by editing chconf.h.
set(KERNSRC
${CHIBIOS}/os/kernel/src/chsys.c
${CHIBIOS}/os/kernel/src/chdebug.c
${CHIBIOS}/os/kernel/src/chlists.c
${CHIBIOS}/os/kernel/src/chvt.c
${CHIBIOS}/os/kernel/src/chschd.c
${CHIBIOS}/os/kernel/src/chthreads.c
${CHIBIOS}/os/kernel/src/chdynamic.c
${CHIBIOS}/os/kernel/src/chregistry.c
${CHIBIOS}/os/kernel/src/chsem.c
${CHIBIOS}/os/kernel/src/chmtx.c
${CHIBIOS}/os/kernel/src/chcond.c
${CHIBIOS}/os/kernel/src/chevents.c
${CHIBIOS}/os/kernel/src/chmsg.c
${CHIBIOS}/os/kernel/src/chmboxes.c
${CHIBIOS}/os/kernel/src/chqueues.c
${CHIBIOS}/os/kernel/src/chmemcore.c
${CHIBIOS}/os/kernel/src/chheap.c
${CHIBIOS}/os/kernel/src/chmempools.c
)
# Required include directories
set(KERNINC
${CHIBIOS}/os/kernel/include
)

Some files were not shown because too many files have changed in this diff Show More