mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-01-11 07:19:34 -05:00
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:
commit
fdfa7c9776
20
.travis.yml
20
.travis.yml
@ -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:
|
||||
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
43
CMakeLists.txt
Normal 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
83
firmware/CMakeLists.txt
Normal 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
|
||||
)
|
334
firmware/application/CMakeLists.txt
Normal file
334
firmware/application/CMakeLists.txt
Normal 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
|
||||
)
|
@ -21,8 +21,6 @@
|
||||
|
||||
#include "ais_app.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
@ -128,15 +126,8 @@ 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);
|
||||
|
||||
@ -146,7 +137,6 @@ void AISLogger::on_packet(const ais::Packet& packet) {
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -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,7 +92,9 @@ 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);
|
||||
|
||||
@ -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);
|
||||
|
@ -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),
|
||||
|
@ -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();
|
||||
|
@ -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 message {
|
||||
SpectrumStreamingConfigMessage::Mode::Running
|
||||
}
|
||||
);
|
||||
};
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
void spectrum_streaming_stop() {
|
||||
shared_memory.baseband_queue.push_and_wait(
|
||||
SpectrumStreamingConfigMessage {
|
||||
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 */
|
||||
|
@ -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__*/
|
||||
|
@ -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 */
|
||||
|
@ -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;
|
||||
|
@ -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 { };
|
||||
}
|
||||
|
@ -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__*/
|
||||
|
@ -41,9 +41,12 @@ 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) {
|
||||
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()), from.base(), from.size);
|
||||
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.
|
||||
@ -52,6 +55,13 @@ void m4_init(const portapack::spi_flash::region_t from, const portapack::memory:
|
||||
|
||||
/* Reset M4 core */
|
||||
LPC_RGU->RESET_CTRL[0] = (1 << 13);
|
||||
|
||||
return;
|
||||
}
|
||||
chunk = chunk->next();
|
||||
}
|
||||
|
||||
chDbgPanic("NoImg");
|
||||
}
|
||||
|
||||
void m4_request_shutdown() {
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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__*/
|
||||
|
@ -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 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() {
|
||||
|
@ -24,6 +24,8 @@
|
||||
|
||||
#include "ui_navigation.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "log_file.hpp"
|
||||
|
||||
#include "ert_packet.hpp"
|
||||
@ -85,7 +87,9 @@ 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);
|
||||
|
||||
@ -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();
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
@ -124,15 +187,15 @@ void EventDispatcher::dispatch(const eventmask_t events) {
|
||||
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);
|
||||
}
|
||||
|
@ -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__*/
|
||||
|
@ -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 */
|
||||
|
@ -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__*/
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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__*/
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
std::string entry = tuning_frequency_str + " FSK 38.4 19.2 " + hex_formatted.data + "/" + hex_formatted.errors;
|
||||
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 {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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__*/
|
||||
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
@ -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",
|
||||
{
|
||||
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>(
|
||||
{
|
||||
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 */
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
366
firmware/baseband/CMakeLists.txt
Normal file
366
firmware/baseband/CMakeLists.txt
Normal 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
|
||||
)
|
74
firmware/baseband/main.cpp → firmware/baseband/baseband.cpp
Executable file → Normal file
74
firmware/baseband/main.cpp → firmware/baseband/baseband.cpp
Executable file → Normal 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;
|
||||
}
|
@ -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(
|
||||
|
@ -31,8 +31,6 @@
|
||||
namespace baseband {
|
||||
namespace dma {
|
||||
|
||||
using Handler = void (*)();
|
||||
|
||||
void init();
|
||||
void configure(
|
||||
baseband::sample_t* const buffer_base,
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
@ -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__*/
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void events_flag_isr(const eventmask_t events) {
|
||||
if( thread_event_loop ) {
|
||||
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;
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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__*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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); }
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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__*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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(),
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 };
|
||||
};
|
||||
|
||||
|
76
firmware/baseband/stream_input.cpp
Normal file
76
firmware/baseband/stream_input.cpp
Normal 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;
|
||||
}
|
@ -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__*/
|
||||
|
@ -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();
|
||||
|
211
firmware/baseband/Makefile → firmware/bootstrap/CMakeLists.txt
Executable file → Normal file
211
firmware/baseband/Makefile → firmware/bootstrap/CMakeLists.txt
Executable file → Normal 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
|
||||
)
|
@ -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
|
@ -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
|
||||
)
|
@ -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) {
|
||||
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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
|
||||
)
|
@ -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:
|
||||
|
29
firmware/chibios/os/hal/hal.cmake
Normal file
29
firmware/chibios/os/hal/hal.cmake
Normal 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
|
||||
)
|
27
firmware/chibios/os/kernel/kernel.cmake
Normal file
27
firmware/chibios/os/kernel/kernel.cmake
Normal 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
Loading…
Reference in New Issue
Block a user