Sync with Sharebrained's fw, only Xylos TX works for now

This commit is contained in:
furrtek 2016-07-27 03:03:40 +02:00
parent fdfa7c9776
commit 739956b42b
150 changed files with 17236 additions and 7875 deletions

View File

@ -1,104 +1,167 @@
#
# 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.
#
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 2.8
PATH_BOOTSTRAP=bootstrap
PATH_APPLICATION=application
PATH_BASEBAND=baseband
PATH_BASEBAND_TX=baseband-tx
# Default target executed when no arguments are given to make.
default_target: all
.PHONY : default_target
TARGET=portapack-h1-firmware
#=============================================================================
# Special targets provided by cmake.
TARGET_BOOTSTRAP=$(PATH_BOOTSTRAP)/bootstrap
TARGET_HACKRF_FIRMWARE=hackrf_one_usb_ram
TARGET_APPLICATION=$(PATH_APPLICATION)/build/application
TARGET_BASEBAND=$(PATH_BASEBAND)/build/baseband
TARGET_BASEBAND_TX=$(PATH_BASEBAND_TX)/build/baseband-tx
# Disable implicit rules so canonical targets will work.
.SUFFIXES:
MAKE_SPI_IMAGE=tools/make_spi_image.py
MAKE_MODULES_FILE=tools/make_baseband_file.py
# Remove some rules from gmake that .SUFFIXES does not remove.
SUFFIXES =
DFU_HACKRF=hackrf_one_usb_ram.dfu
LICENSE=../LICENSE
.SUFFIXES: .hpux_make_needs_suffix_list
GIT_REVISION=$(shell git log -n 1 --format=%h)
# Suppress display of executed commands.
$(VERBOSE).SILENT:
CP=arm-none-eabi-objcopy
# A target that is always out of date.
cmake_force:
.PHONY : cmake_force
MODULES = $(PATH_BASEBAND) \
$(PATH_BASEBAND_TX)
#=============================================================================
# Set environment variables for the build.
all: $(TARGET).bin modules
# The shell in which to execute make rules.
SHELL = /bin/sh
release: $(TARGET).bin $(DFU_HACKRF) $(LICENSE)
# TODO: Bad hack to fix location of LICENSE file for tar.
cp $(LICENSE) LICENSE
tar -c -j -f $(TARGET)-$(GIT_REVISION).tar.bz2 $(TARGET).bin $(DFU_HACKRF) LICENSE
zip -9 -q $(TARGET)-$(GIT_REVISION).zip $(TARGET).bin $(DFU_HACKRF) LICENSE
rm -f LICENSE
# The CMake executable.
CMAKE_COMMAND = /usr/bin/cmake
program: $(TARGET).bin modules
dfu-util --device 1fc9:000c --download hackrf_one_usb_ram.dfu --reset
sleep 1s
hackrf_spiflash -w $(TARGET).bin
modules: $(TARGET_BASEBAND).bin $(TARGET_BASEBAND_TX).bin
$(MAKE_MODULES_FILE) $(MODULES)
cp $(PATH_BASEBAND).bin ../sdcard/$(PATH_BASEBAND).bin
cp $(PATH_BASEBAND_TX).bin ../sdcard/$(PATH_BASEBAND_TX).bin
# The command to remove a file.
RM = /usr/bin/cmake -E remove -f
$(TARGET).bin: modules $(MAKE_SPI_IMAGE) $(TARGET_BOOTSTRAP).bin $(TARGET_HACKRF_FIRMWARE).dfu $(TARGET_BASEBAND_TX)_inc.bin $(TARGET_APPLICATION).bin
$(MAKE_SPI_IMAGE) $(TARGET_BOOTSTRAP).bin $(TARGET_HACKRF_FIRMWARE).dfu $(TARGET_BASEBAND_TX)_inc.bin $(TARGET_APPLICATION).bin $(TARGET).bin
# Escaping for special characters.
EQUALS = =
$(TARGET_BOOTSTRAP).bin: $(TARGET_BOOTSTRAP).elf
$(CP) -O binary $(TARGET_BOOTSTRAP).elf $(TARGET_BOOTSTRAP).bin
# The top-level source directory on which CMake was run.
CMAKE_SOURCE_DIR = /home/furrtek/portapack-hackrf
$(TARGET_BASEBAND).bin: $(TARGET_BASEBAND).elf
$(CP) -O binary $(TARGET_BASEBAND).elf $(TARGET_BASEBAND).bin
$(TARGET_BASEBAND_TX).bin: $(TARGET_BASEBAND_TX).elf
$(CP) -O binary $(TARGET_BASEBAND_TX).elf $(TARGET_BASEBAND_TX).bin
# The top-level build directory on which CMake was run.
CMAKE_BINARY_DIR = /home/furrtek/portapack-hackrf
$(TARGET_APPLICATION).bin: $(TARGET_APPLICATION).elf
$(CP) -O binary $(TARGET_APPLICATION).elf $(TARGET_APPLICATION).bin
#=============================================================================
# Targets provided globally by CMake.
$(TARGET_BASEBAND).elf: always_check
@$(MAKE) -s -e GIT_REVISION=$(GIT_REVISION) -C $(PATH_BASEBAND)
$(TARGET_BASEBAND_TX).elf: always_check
@$(MAKE) -s -e GIT_REVISION=$(GIT_REVISION) -C $(PATH_BASEBAND_TX)
# Special rule for the target edit_cache
edit_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running interactive CMake command-line interface..."
/usr/bin/cmake -i .
.PHONY : edit_cache
$(TARGET_APPLICATION).elf: always_check
@$(MAKE) -s -e GIT_REVISION=$(GIT_REVISION) -C $(PATH_APPLICATION)
# Special rule for the target edit_cache
edit_cache/fast: edit_cache
.PHONY : edit_cache/fast
$(TARGET_BOOTSTRAP).elf: always_check
@$(MAKE) -s -e GIT_REVISION=$(GIT_REVISION) -C $(PATH_BOOTSTRAP)
# Special rule for the target rebuild_cache
rebuild_cache:
@$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..."
/usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR)
.PHONY : rebuild_cache
# Special rule for the target rebuild_cache
rebuild_cache/fast: rebuild_cache
.PHONY : rebuild_cache/fast
# The main all target
all: cmake_check_build_system
cd /home/furrtek/portapack-hackrf && $(CMAKE_COMMAND) -E cmake_progress_start /home/furrtek/portapack-hackrf/CMakeFiles /home/furrtek/portapack-hackrf/firmware/CMakeFiles/progress.marks
cd /home/furrtek/portapack-hackrf && $(MAKE) -f CMakeFiles/Makefile2 firmware/all
$(CMAKE_COMMAND) -E cmake_progress_start /home/furrtek/portapack-hackrf/CMakeFiles 0
.PHONY : all
# The main clean target
clean:
rm -f $(TARGET).bin
rm -f $(TARGET_BOOTSTRAP).bin
rm -f $(TARGET_BASEBAND).bin
rm -f $(TARGET_APPLICATION).bin
$(MAKE) -C $(PATH_BASEBAND) clean
$(MAKE) -C $(PATH_APPLICATION) clean
$(MAKE) -C $(PATH_BOOTSTRAP) clean
cd /home/furrtek/portapack-hackrf && $(MAKE) -f CMakeFiles/Makefile2 firmware/clean
.PHONY : clean
# The main clean target
clean/fast: clean
.PHONY : clean/fast
# Prepare targets for installation.
preinstall: all
cd /home/furrtek/portapack-hackrf && $(MAKE) -f CMakeFiles/Makefile2 firmware/preinstall
.PHONY : preinstall
# Prepare targets for installation.
preinstall/fast:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f CMakeFiles/Makefile2 firmware/preinstall
.PHONY : preinstall/fast
# clear depends
depend:
cd /home/furrtek/portapack-hackrf && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1
.PHONY : depend
# Convenience name for target.
firmware/CMakeFiles/firmware.dir/rule:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f CMakeFiles/Makefile2 firmware/CMakeFiles/firmware.dir/rule
.PHONY : firmware/CMakeFiles/firmware.dir/rule
# Convenience name for target.
firmware: firmware/CMakeFiles/firmware.dir/rule
.PHONY : firmware
# fast build rule for target.
firmware/fast:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/CMakeFiles/firmware.dir/build.make firmware/CMakeFiles/firmware.dir/build
.PHONY : firmware/fast
# Convenience name for target.
firmware/CMakeFiles/program.dir/rule:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f CMakeFiles/Makefile2 firmware/CMakeFiles/program.dir/rule
.PHONY : firmware/CMakeFiles/program.dir/rule
# Convenience name for target.
program: firmware/CMakeFiles/program.dir/rule
.PHONY : program
# fast build rule for target.
program/fast:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/CMakeFiles/program.dir/build.make firmware/CMakeFiles/program.dir/build
.PHONY : program/fast
# Convenience name for target.
firmware/CMakeFiles/release.dir/rule:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f CMakeFiles/Makefile2 firmware/CMakeFiles/release.dir/rule
.PHONY : firmware/CMakeFiles/release.dir/rule
# Convenience name for target.
release: firmware/CMakeFiles/release.dir/rule
.PHONY : release
# fast build rule for target.
release/fast:
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/CMakeFiles/release.dir/build.make firmware/CMakeFiles/release.dir/build
.PHONY : release/fast
# Help Target
help:
@echo "The following are some of the valid targets for this Makefile:"
@echo "... all (the default if no target is provided)"
@echo "... clean"
@echo "... depend"
@echo "... edit_cache"
@echo "... firmware"
@echo "... program"
@echo "... rebuild_cache"
@echo "... release"
.PHONY : help
#=============================================================================
# Special targets to cleanup operation of make.
# Special rule to run CMake to check the build system integrity.
# No rule that depends on this can have commands that come from listfiles
# because they might be regenerated.
cmake_check_build_system:
cd /home/furrtek/portapack-hackrf && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0
.PHONY : cmake_check_build_system
always_check:
@true

View File

@ -141,6 +141,7 @@ set(CPPSRC
${COMMON}/ui_widget.cpp
${COMMON}/ui_painter.cpp
${COMMON}/ui_focus.cpp
${COMMON}/msgpack.cpp
ui_navigation.cpp
ui_menu.cpp
ui_rssi.cpp
@ -155,9 +156,23 @@ set(CPPSRC
ui_console.cpp
ui_receiver.cpp
ui_record_view.cpp
ui_textentry.cpp
ui_alphanum.cpp
ui_spectrum.cpp
ui_about.cpp
ui_jammer.cpp
ui_handwrite.cpp
ui_afsksetup.cpp
ui_closecall.cpp
ui_soundboard.cpp
ui_rds.cpp
ui_lcr.cpp
ui_xylos.cpp
ui_freqman.cpp
# ui_loadmodule.cpp
recent_entries.cpp
receiver_model.cpp
transmitter_model.cpp
spectrum_color_lut.cpp
analog_audio_app.cpp
${COMMON}/ais_baseband.cpp

5010
firmware/application/Makefile Executable file → Normal file

File diff suppressed because it is too large Load Diff

View File

@ -78,6 +78,13 @@ void WFMConfig::apply() const {
audio::set_rate(audio::Rate::Hz_48000);
}
void set_xylos_data(const char ccir_message[]) {
const XylosConfigureMessage message {
ccir_message
};
send_message(&message);
}
static bool baseband_image_running = false;
void run_image(const portapack::spi_flash::image_tag_t image_tag) {
@ -108,15 +115,6 @@ void shutdown() {
baseband_image_running = false;
}
void spectrum_streaming_start(size_t decimation_factor) {
shared_memory.baseband_queue.push_and_wait(
SpectrumStreamingConfigMessage {
SpectrumStreamingConfigMessage::Mode::Running,
decimation_factor
}
);
}
void spectrum_streaming_start() {
SpectrumStreamingConfigMessage message {
SpectrumStreamingConfigMessage::Mode::Running

View File

@ -52,10 +52,11 @@ struct WFMConfig {
void apply() const;
};
void set_xylos_data(const char ccir_message[]);
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();

View File

@ -28,12 +28,9 @@ using namespace lpc43xx;
#include "message.hpp"
#include "baseband_api.hpp"
#include "portapack_shared_memory.hpp"
#include <cstring>
char * modhash;
/* TODO: OK, this is cool, but how do I put the M4 to sleep so I can switch to
* a different image? Other than asking the old image to sleep while the M0
* makes changes?
@ -74,55 +71,3 @@ void m0_halt() {
port_wait_for_interrupt();
}
}
int m4_load_image(void) {
uint32_t mod_size;
UINT bw;
uint8_t i;
char md5sum[16];
FILINFO modinfo;
FIL modfile;
DIR rootdir;
FRESULT res;
// Scan SD card root directory for files with the right md5 fingerprint at the right location
f_opendir(&rootdir, "/");
for (;;) {
res = f_readdir(&rootdir, &modinfo);
if (res != FR_OK || modinfo.fname[0] == 0) break;
if (!(modinfo.fattrib & AM_DIR)) {
f_open(&modfile, modinfo.fname, FA_OPEN_EXISTING | FA_READ);
f_lseek(&modfile, 26);
f_read(&modfile, &md5sum, 16, &bw);
for (i = 0; i < 16; i++) {
if (md5sum[i] != modhash[i]) break;
}
if (i == 16) {
f_lseek(&modfile, 6);
f_read(&modfile, &mod_size, 4, &bw);
f_lseek(&modfile, 256);
f_read(&modfile, reinterpret_cast<void*>(portapack::memory::map::m4_code.base()), mod_size, &bw);
LPC_RGU->RESET_CTRL[0] = (1 << 13);
f_close(&modfile);
return 1;
}
f_close(&modfile);
}
}
return 0;
}
void m4_switch(const char * hash) {
modhash = const_cast<char*>(hash);
// Ask M4 to enter loop in RAM
BasebandConfiguration baseband_switch {
.mode = 0xFF,
.sampling_rate = 0,
.decimation_factor = 1,
};
BasebandConfigurationMessage message { baseband_switch };
shared_memory.baseband_queue.push(message);
}

View File

@ -24,15 +24,11 @@
#include <cstddef>
#include "ff.h"
#include "memory_map.hpp"
#include "spi_image.hpp"
#include "ui_navigation.hpp"
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);
void m0_halt();

View File

@ -22,6 +22,7 @@
#include "event_m0.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
#include "sd_card.hpp"
#include "time.hpp"

View File

@ -106,7 +106,7 @@ void set_direction(const rf::Direction new_direction) {
/* TODO: Refactor all the various "Direction" enumerations into one. */
/* TODO: Only make changes if direction changes, but beware of clock enabling. */
direction = new_direction;
second_if.set_mode((direction == rf::Direction::Transmit) ? max2837::Mode::Transmit : max2837::Mode::Receive);
rf_path.set_direction(direction);

View File

@ -78,6 +78,11 @@ uint32_t TransmitterModel::sampling_rate() const {
return baseband_configuration.sampling_rate;
}
uint32_t TransmitterModel::baseband_oversampling() const {
// TODO: Rename decimation_factor.
return baseband_configuration.decimation_factor;
}
void TransmitterModel::enable() {
enabled_ = true;
radio::set_direction(rf::Direction::Transmit);
@ -91,20 +96,14 @@ void TransmitterModel::enable() {
void TransmitterModel::disable() {
enabled_ = false;
baseband::stop();
// TODO: Responsibility for enabling/disabling the radio is muddy.
// Some happens in ReceiverModel, some inside radio namespace.
radio::disable();
}
uint32_t TransmitterModel::baseband_oversampling() const {
// TODO: Rename decimation_factor.
return baseband_configuration.decimation_factor;
}
void TransmitterModel::update_tuning_frequency() {
radio::set_tuning_frequency(tuning_frequency());
radio::set_tuning_frequency(persistent_memory::tuned_frequency());
}
void TransmitterModel::update_rf_amp() {
@ -123,10 +122,6 @@ void TransmitterModel::update_vga() {
radio::set_vga_gain(vga_gain_db_);
}
uint32_t TransmitterModel::modulation() const {
return baseband_configuration.mode;
}
void TransmitterModel::set_baseband_configuration(const BasebandConfiguration config) {
baseband_configuration = config;
update_baseband_configuration();
@ -138,11 +133,7 @@ void TransmitterModel::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);
}

View File

@ -25,7 +25,7 @@
#include <cstdint>
#include <cstddef>
#include "clock_manager.hpp"
#include "receiver_model.hpp"
#include "message.hpp"
#include "rf_path.hpp"
#include "max2837.hpp"

View File

@ -25,9 +25,11 @@
#include "demofont.hpp"
#include "ymdata.hpp"
#include "cpld_update.hpp"
#include "portapack.hpp"
#include "audio.hpp"
#include "event_m0.hpp"
#include "baseband_api.hpp"
#include "ui_about.hpp"
#include "touch.hpp"
@ -46,42 +48,8 @@ using namespace portapack;
namespace ui {
void AboutView::on_show() {
// Just in case
EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync);
// "Vertical blank interrupt"
EventDispatcher::message_map().register_handler(Message::ID::DisplayFrameSync,
[this](const Message* const) {
update();
}
);
// Just in case
EventDispatcher::message_map().unregister_handler(Message::ID::FIFOSignal);
// Handle baseband asking to fill up FIFO
EventDispatcher::message_map().register_handler(Message::ID::FIFOSignal,
[this](Message* const p) {
FIFODataMessage datamessage;
const auto message = static_cast<const FIFOSignalMessage*>(p);
if (message->signaltype == 1) {
//debug_cnt++;
//if (debug_cnt == 250) for(;;) {}
render_audio();
datamessage.data = ym_buffer;
shared_memory.baseband_queue.push(datamessage);
}
}
);
transmitter_model.set_tuning_frequency(92200000); // 92.2MHz, change !
transmitter_model.set_baseband_configuration({
.mode = 0,
.sampling_rate = 1536000,
.decimation_factor = 1,
});
transmitter_model.set_rf_amp(true);
transmitter_model.enable();
about_radio_config.tuning_frequency = 92200000; // 92.2MHz, change !
radio::enable(about_radio_config);
//audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
}
@ -384,22 +352,20 @@ AboutView::AboutView(
{
uint8_t p, c;
/*
transmitter_model.set_baseband_configuration({
.mode = 0,
.sampling_rate = 1536000,
.decimation_factor = 1,
});
*/
add_children({ {
&text_title,
&text_firmware,
&text_cpld_hackrf,
&text_cpld_portapack,
&text_cpld_hackrf_status,
&button_ok,
} });
if( cpld_hackrf_verify_eeprom() ) {
text_cpld_hackrf_status.set(" OK");
} else {
text_cpld_hackrf_status.set("BAD");
}
// Politely ask for about 26kB
framebuffer = (ui::Color *)chHeapAlloc(0x0, 180 * 72 * sizeof(ui::Color));
@ -422,14 +388,13 @@ AboutView::AboutView(
ym_init();
button_ok.on_select = [this,&nav](Button&){
EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync);
if (framebuffer) chHeapFree(framebuffer); // Do NOT forget this
nav.pop();
};
}
AboutView::~AboutView() {
transmitter_model.disable();
radio::disable();
}
void AboutView::focus() {

View File

@ -48,6 +48,17 @@ private:
void draw_demoglyph(ui::Point p, char ch, ui::Color * pal);
uint16_t debug_cnt = 0;
radio::Configuration about_radio_config = {
0,
1536000, // ?
2500000, // ?
rf::Direction::Transmit,
true,
0,
0,
1,
};
typedef struct ymreg_t {
uint8_t value;
uint8_t cnt;
@ -127,24 +138,47 @@ private:
};
Text text_firmware {
{ 0, 224, 240, 16 },
"Git Commit hash " GIT_REVISION,
{ 0, 128, 240, 16 },
"Git Commit Hash " GIT_REVISION,
};
Text text_cpld_hackrf {
{ 0, 240, 240, 16 },
"HackRF CPLD CRC 0x????????",
{ 0, 144, 11*8, 16 },
"HackRF CPLD",
};
Text text_cpld_portapack {
{ 0, 256, 240, 16 },
"PortaPack CPLD CRC 0x????????",
Text text_cpld_hackrf_status {
{ 240 - 3*8, 144, 3*8, 16 },
"???"
};
Button button_ok {
{ 72, 272, 96, 24 },
"OK"
};
MessageHandlerRegistration message_handler_update {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->update();
}
};
MessageHandlerRegistration message_handler_fifo_signal {
Message::ID::FIFOSignal,
[this](const Message* const p) {
FIFODataMessage datamessage;
const auto message = static_cast<const FIFOSignalMessage*>(p);
if (message->signaltype == 1) {
//debug_cnt++;
//if (debug_cnt == 250) for(;;) {}
this->render_audio();
datamessage.data = ym_buffer;
EventDispatcher::send_message(datamessage);
}
}
};
};
} /* namespace ui */

View File

@ -54,7 +54,6 @@ void AFSKSetupView::updfreq(rf::Frequency f) {
char finalstr[9] = {0};
portapack::persistent_memory::set_tuned_frequency(f);
transmitter_model.set_tuning_frequency(f);
auto mhz = to_string_dec_int(f / 1000000, 3);
auto hz100 = to_string_dec_int((f / 100) % 10000, 4, '0');
@ -106,7 +105,7 @@ AFSKSetupView::AFSKSetupView(
field_repeat.set_value(rpt);
button_setfreq.on_select = [this,&nav](Button&){
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency());
auto new_view = nav.push<FrequencyKeypadView>(portapack::persistent_memory::tuned_frequency());
new_view->on_changed = [this](rf::Frequency f) {
updfreq(f);
};

View File

@ -30,7 +30,6 @@
#include "rf_path.hpp"
#include "max2837.hpp"
#include "volume.hpp"
#include "transmitter_model.hpp"
namespace ui {

View File

@ -159,7 +159,7 @@ void CloseCallView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
uint8_t threshold;
size_t i, m;
baseband::spectrum_streaming_stop;
baseband::spectrum_streaming_stop();
// Spectrum line (for debug)
std::array<Color, 240> pixel_row;
@ -228,35 +228,15 @@ void CloseCallView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
do_detection();
}
baseband::spectrum_streaming_start(1);
baseband::spectrum_streaming_start();
}
void CloseCallView::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(1);
baseband::spectrum_streaming_start();
}
void CloseCallView::on_hide() {
baseband::spectrum_streaming_stop();
EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync);
EventDispatcher::message_map().unregister_handler(Message::ID::ChannelSpectrumConfig);
}
void CloseCallView::on_range_changed() {
@ -455,7 +435,7 @@ CloseCallView::CloseCallView(
.decimation_factor = 1,
});
receiver_model.set_baseband_bandwidth(CC_SLICE_WIDTH);
receiver_model.enable();
//receiver_model.enable();
}
} /* namespace ui */

View File

@ -166,6 +166,25 @@ private:
{ 92, 264, 56, 32 },
"Exit"
};
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);
}
}
}
};
};
} /* namespace ui */

View File

@ -376,20 +376,9 @@ void HandWriteView::sample_pen() {
}
void HandWriteView::on_show() {
// Use screen refresh rate as sampling frequency
EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync);
EventDispatcher::message_map().register_handler(Message::ID::DisplayFrameSync,
[this](const Message* const) {
sample_pen();
}
);
clear_zone(Color::black(), false);
}
void HandWriteView::on_hide() {
EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync);
}
char * HandWriteView::value() {
return txtinput;
}

View File

@ -41,7 +41,6 @@ public:
void paint(Painter& painter) override;
void on_show() override;
void on_hide() override;
bool on_touch(const TouchEvent event) override;
char * value();
@ -89,6 +88,13 @@ private:
void on_button(Button& button);
void update_text();
MessageHandlerRegistration message_handler_sample {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->sample_pen();
}
};
};
} /* namespace ui */

View File

@ -24,7 +24,7 @@
#include "ch.h"
#include "evtimer.h"
#include "baseband_api.hpp"
#include "ff.h"
#include "hackrf_gpio.hpp"
#include "portapack.hpp"
@ -48,11 +48,8 @@ void JammerView::focus() {
}
JammerView::~JammerView() {
transmitter_model.disable();
}
void JammerView::paint(Painter& painter) {
(void)painter;
radio::disable();
baseband::shutdown();
}
void JammerView::updfreq(uint8_t id, rf::Frequency f) {
@ -147,10 +144,14 @@ void JammerView::updfreq(uint8_t id, rf::Frequency f) {
}
}
JammerView::JammerView(
NavigationView& nav
)
{
void JammerView::on_retune(const int64_t freq) {
if (freq > 0) {
radio::set_tuning_frequency(freq);
}
}
JammerView::JammerView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_jammer);
static constexpr Style style_val {
.font = font::fixed_8x16,
@ -170,12 +171,6 @@ JammerView::JammerView(
.foreground = Color::grey(),
};
transmitter_model.set_baseband_configuration({
.mode = 6,
.sampling_rate = 1536000, // ?
.decimation_factor = 1,
});
add_children({ {
&text_type,
&options_modulation,
@ -237,16 +232,6 @@ JammerView::JammerView(
button_transmit.on_select = [this](Button&) {
uint8_t i = 0;
rf::Frequency t, range_lower;
EventDispatcher::message_map().unregister_handler(Message::ID::Retune);
EventDispatcher::message_map().register_handler(Message::ID::Retune,
[this](Message* const p) {
const auto message = static_cast<const RetuneMessage*>(p);
if (message->freq > 0) {
transmitter_model.set_tuning_frequency(message->freq);
}
}
);
for (i = 0; i < 16; i++) {
shared_memory.jammer_ranges[i].active = false;
@ -327,18 +312,16 @@ JammerView::JammerView(
}
// }
transmitter_model.set_tuning_frequency(shared_memory.jammer_ranges[0].center);
if (jamming == true) {
jamming = false;
button_transmit.set_style(&style_val);
button_transmit.set_text("START");
transmitter_model.disable();
radio::disable();
} else {
jamming = true;
button_transmit.set_style(&style_cancel);
button_transmit.set_text("STOP");
transmitter_model.enable();
radio::disable();
}
};
@ -346,6 +329,16 @@ JammerView::JammerView(
nav.pop();
};
radio::enable({
shared_memory.jammer_ranges[0].center,
1536000, // ?
2500000, // ?
rf::Direction::Transmit,
true,
static_cast<int8_t>(receiver_model.lna()),
static_cast<int8_t>(receiver_model.vga()),
1,
});
}
} /* namespace ui */

View File

@ -30,7 +30,6 @@
#include "rf_path.hpp"
#include "max2837.hpp"
#include "volume.hpp"
#include "transmitter_model.hpp"
namespace ui {
@ -41,9 +40,12 @@ public:
void updfreq(uint8_t id, rf::Frequency f);
void focus() override;
void paint(Painter& painter) override;
std::string title() const override { return "Jammer"; };
private:
void on_retune(const int64_t freq);
rf::Frequency range1_min;
rf::Frequency range1_max;
rf::Frequency range2_min;
@ -253,6 +255,14 @@ private:
{ 21 * 8, 16 * 16, 64, 32 },
"Exit"
};
MessageHandlerRegistration message_handler_retune {
Message::ID::Retune,
[this](Message* const p) {
const auto message = static_cast<const RetuneMessage*>(p);
this->on_retune(message->freq);
}
};
};
} /* namespace ui */

View File

@ -28,6 +28,7 @@
#include "ch.h"
#include "ff.h"
#include "baseband_api.hpp"
#include "hackrf_gpio.hpp"
#include "portapack.hpp"
#include "event_m0.hpp"
@ -51,7 +52,7 @@ void LCRView::focus() {
}
LCRView::~LCRView() {
transmitter_model.disable();
radio::disable();
}
void LCRView::make_frame() {
@ -216,6 +217,69 @@ void LCRView::paint(Painter& painter) {
text_recap.set(finalstr);
}
void LCRView::on_txdone(int n) {
char str[16];
if (abort_scan) {
text_status.set(" ");
strcpy(str, "Abort @");
strcat(str, rgsb);
text_status.set(str);
progress.set_value(0);
radio::disable();
txing = false;
scanning = false;
abort_scan = false;
button_scan.set_style(&style_val);
button_scan.set_text("SCAN");
return;
}
if (n > 0) {
if (scanning) {
scan_progress += 0.555f;
progress.set_value(scan_progress);
} else {
text_status.set(" ");
strcpy(str, to_string_dec_int(6 - n).c_str());
strcat(str, "/5");
text_status.set(str);
progress.set_value((6 - n) * 20);
}
} else {
if (scanning && (scan_index < 36)) {
radio::disable();
// Next address
strcpy(rgsb, RGSB_list[scan_index]);
make_frame();
memset(shared_memory.radio_data, 0, 256);
memcpy(shared_memory.radio_data, lcrframe_f, 256);
shared_memory.afsk_transmit_done = false;
shared_memory.afsk_repeat = 5;
text_status.set(" ");
strcpy(str, to_string_dec_int(scan_index).c_str());
strcat(str, "/36");
text_status.set(str);
scan_progress += 0.694f;
progress.set_value(scan_progress);
scan_index++;
radio::disable();
} else {
text_status.set("Ready ");
progress.set_value(0);
radio::disable();
txing = false;
scanning = false;
button_scan.set_style(&style_val);
button_scan.set_text("SCAN");
}
}
}
void LCRView::start_tx() {
char str[16];
@ -226,7 +290,7 @@ void LCRView::start_tx() {
make_frame();
transmitter_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency());
lcr_radio_config.tuning_frequency = portapack::persistent_memory::tuned_frequency();
shared_memory.afsk_samples_per_bit = 228000 / portapack::persistent_memory::afsk_bitrate();
shared_memory.afsk_phase_inc_mark = portapack::persistent_memory::afsk_mark_freq() * (0x40000 * 256) / 2280;
@ -244,74 +308,6 @@ void LCRView::start_tx() {
shared_memory.afsk_transmit_done = false;
shared_memory.afsk_repeat = 5; //(portapack::persistent_memory::afsk_config() >> 8) & 0xFF;
EventDispatcher::message_map().unregister_handler(Message::ID::TXDone);
EventDispatcher::message_map().register_handler(Message::ID::TXDone,
[this](Message* const p) {
char str[16];
if (abort_scan) {
text_status.set(" ");
strcpy(str, "Abort @");
strcat(str, rgsb);
text_status.set(str);
progress.set_value(0);
transmitter_model.disable();
txing = false;
scanning = false;
abort_scan = false;
button_scan.set_style(&style_val);
button_scan.set_text("SCAN");
return;
}
const auto message = static_cast<const TXDoneMessage*>(p);
if (message->n > 0) {
if (scanning) {
scan_progress += 0.555f;
progress.set_value(scan_progress);
} else {
text_status.set(" ");
strcpy(str, to_string_dec_int(6 - message->n).c_str());
strcat(str, "/5");
text_status.set(str);
progress.set_value((6 - message->n) * 20);
}
} else {
if (scanning && (scan_index < 36)) {
transmitter_model.disable();
// Next address
strcpy(rgsb, RGSB_list[scan_index]);
make_frame();
memset(shared_memory.radio_data, 0, 256);
memcpy(shared_memory.radio_data, lcrframe_f, 256);
shared_memory.afsk_transmit_done = false;
shared_memory.afsk_repeat = 5;
text_status.set(" ");
strcpy(str, to_string_dec_int(scan_index).c_str());
strcat(str, "/36");
text_status.set(str);
scan_progress += 0.694f;
progress.set_value(scan_progress);
scan_index++;
transmitter_model.enable();
} else {
text_status.set("Ready ");
progress.set_value(0);
transmitter_model.disable();
txing = false;
scanning = false;
button_scan.set_style(&style_val);
button_scan.set_text("SCAN");
}
}
}
);
if (scanning) {
text_status.set(" ");
@ -327,18 +323,11 @@ void LCRView::start_tx() {
}
txing = true;
transmitter_model.enable();
radio::enable(lcr_radio_config);
}
LCRView::LCRView(
NavigationView& nav
)
{
transmitter_model.set_baseband_configuration({
.mode = 3,
.sampling_rate = 2280000,
.decimation_factor = 1,
});
LCRView::LCRView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_lcr);
memset(litteral, 0, 5 * 8);
memset(rgsb, 0, 5);

View File

@ -73,6 +73,18 @@ private:
void make_frame();
void start_tx();
void on_txdone(int n);
radio::Configuration lcr_radio_config = {
0,
2280000, // ?
2500000, // ?
rf::Direction::Transmit,
true,
0,
0,
1,
};
// 2: 94 ?
// 9: 85 ?
@ -113,12 +125,12 @@ private:
};
OptionsField options_ec {
{ 20 * 8, 1 * 16 },
4,
{ 19 * 8, 6 },
7,
{
{ "Auto", 0 },
{ "Jour", 1 },
{ "Nuit", 2 }
{ "EC:Auto", 0 },
{ "EC:Jour", 1 },
{ "EC:Nuit", 2 }
}
};
@ -206,6 +218,14 @@ private:
{ 160, 270, 64, 32 },
"CLEAR"
};
MessageHandlerRegistration message_handler_tx_done {
Message::ID::TXDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
this->on_txdone(message.n);
}
};
};
} /* namespace ui */

View File

@ -54,11 +54,6 @@ void LoadModuleView::focus() {
button_ok.focus();
}
void LoadModuleView::on_hide() {
EventDispatcher::message_map().unregister_handler(Message::ID::ReadyForSwitch);
EventDispatcher::message_map().unregister_handler(Message::ID::ModuleID);
}
void LoadModuleView::on_show() {
char md5_signature[16];
uint8_t c;
@ -129,9 +124,7 @@ int LoadModuleView::load_image() {
}
void LoadModuleView::loadmodule() {
//message_map.unregister_handler(Message::ID::ReadyForSwitch);
m4_switch(_hash);
//baseband::shutdown();
/*EventDispatcher::message_map().register_handler(Message::ID::ReadyForSwitch,
[this](Message* const p) {

View File

@ -49,7 +49,6 @@ public:
void loadmodule();
void on_show() override;
void on_hide() override;
void focus() override;
private:

View File

@ -20,7 +20,7 @@
*/
#include "ui_navigation.hpp"
#include "ui_loadmodule.hpp"
//#include "ui_loadmodule.hpp"
#include "modules.h"
@ -38,6 +38,16 @@
#include "ui_closecall.hpp" // DEBUG
#include "ui_freqman.hpp" // DEBUG
#include "ui_setup.hpp"
#include "ui_debug.hpp"
#include "ui_rds.hpp"
#include "ui_xylos.hpp"
#include "ui_epar.hpp"
#include "ui_lcr.hpp"
#include "analog_audio_app.hpp"
#include "ui_audiotx.hpp"
#include "ui_jammer.hpp"
#include "analog_audio_app.hpp"
#include "ais_app.hpp"
#include "ert_app.hpp"
@ -230,16 +240,16 @@ ReceiverMenuView::ReceiverMenuView(NavigationView& nav) {
SystemMenuView::SystemMenuView(NavigationView& nav) {
add_items<12>({ {
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(false); } },
{ "Receiver RX", ui::Color::cyan(), [&nav](){ nav.push<LoadModuleView>(md5_baseband, Receiver); } },
{ "Close Call RX", ui::Color::cyan(), [&nav](){ nav.push<LoadModuleView>(md5_baseband, CloseCall); } },
{ "Pokemon GO Away TX", ui::Color::blue(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, Jammer); } },
{ "Receiver RX", ui::Color::cyan(), [&nav](){ nav.push<ReceiverMenuView>(); } },
{ "Close Call RX", ui::Color::cyan(), [&nav](){ nav.push<CloseCallView>(); } },
{ "Pokemon GO Away TX", ui::Color::blue(), [&nav](){ nav.push<JammerView>(); } },
//{ "Soundboard TX", ui::Color::yellow(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, SoundBoard); } },
//{ "Audio TX", ui::Color::yellow(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, AudioTX); } },
{ "Frequency manager", ui::Color::white(), [&nav](){ nav.push<FreqManView>(); } },
//{ "EPAR TX", ui::Color::green(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, EPAR); } },
{ "Xylos TX", ui::Color::green(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, Xylos); } },
{ "TEDI/LCR TX", ui::Color::orange(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, LCR); } },
{ "RDS TX", ui::Color::yellow(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, RDS); } },
{ "Xylos TX", ui::Color::green(), [&nav](){ nav.push<XylosView>(); } },
{ "TEDI/LCR TX", ui::Color::orange(), [&nav](){ nav.push<LCRView>(); } },
{ "RDS TX", ui::Color::yellow(), [&nav](){ nav.push<RDSView>(); } },
//{ "Capture", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } },
//{ "Analyze", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } },
{ "Setup", ui::Color::white(), [&nav](){ nav.push<SetupMenuView>(); } },

View File

@ -27,6 +27,7 @@
#include "hackrf_gpio.hpp"
#include "portapack.hpp"
#include "radio.hpp"
#include "baseband_api.hpp"
#include "hackrf_hal.hpp"
#include "portapack_shared_memory.hpp"
@ -42,7 +43,8 @@ void RDSView::focus() {
}
RDSView::~RDSView() {
transmitter_model.disable();
radio::disable();
baseband::shutdown();
}
std::string to_string_bin(const uint32_t n, const uint8_t l) {
@ -213,15 +215,8 @@ void RDSView::paint(Painter& painter) {
text_radiotextb.set(&RadioText[16]);
}
RDSView::RDSView(
NavigationView& nav
)
{
transmitter_model.set_baseband_configuration({
.mode = 5,
.sampling_rate = 2280000,
.decimation_factor = 1,
});
RDSView::RDSView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_rds);
strcpy(PSN, "TEST1234");
strcpy(RadioText, "Radiotext test !");
@ -242,10 +237,10 @@ RDSView::RDSView(
&button_exit
} });
field_frequency.set_value(transmitter_model.tuning_frequency());
rds_radio_config.tuning_frequency = tuning_frequency;
field_frequency.set_step(100000);
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency());
auto new_view = nav.push<FrequencyKeypadView>(tuning_frequency);
new_view->on_changed = [this](rf::Frequency f) {
this->field_frequency.set_value(f);
};
@ -260,16 +255,16 @@ RDSView::RDSView(
};
button_txpsn.on_select = [this](Button&){
if (txing) {
transmitter_model.disable();
radio::disable();
button_txpsn.set_text("PSN");
button_txradiotext.set_text("Radiotext");
txing = false;
} else {
gen_PSN(PSN);
transmitter_model.set_tuning_frequency(field_frequency.value());
rds_radio_config.tuning_frequency = tuning_frequency;
button_txpsn.set_text("STOP");
txing = true;
transmitter_model.enable();
radio::disable();
}
};
@ -278,16 +273,16 @@ RDSView::RDSView(
};
button_txradiotext.on_select = [this](Button&){
if (txing) {
transmitter_model.disable();
radio::disable();
button_txpsn.set_text("PSN");
button_txradiotext.set_text("Radiotext");
txing = false;
} else {
gen_RadioText(RadioText);
transmitter_model.set_tuning_frequency(field_frequency.value());
rds_radio_config.tuning_frequency = tuning_frequency;
button_txradiotext.set_text("STOP");
txing = true;
transmitter_model.enable();
radio::enable(rds_radio_config);
}
};

View File

@ -54,6 +54,7 @@ private:
char PSN[9];
char RadioText[25];
bool txing = false;
int64_t tuning_frequency = 92200000; // TODO: CHANGE !
uint8_t b2b(const bool in);
@ -64,6 +65,17 @@ private:
const bool MS, const bool DI, const uint8_t C, const char * chars);
void make_2A_group(uint32_t group[], const uint16_t PI_code, const bool TP, const uint8_t PTY, const bool AB,
const bool segment, const char * chars);
radio::Configuration rds_radio_config = {
0,
2280000, // ?
2500000, // ?
rf::Direction::Transmit,
true,
0,
0,
1,
};
FrequencyField field_frequency {
{ 1 * 8, 1 * 16 },

View File

@ -27,8 +27,11 @@ using namespace lpc43xx;
#include "portapack.hpp"
using portapack::receiver_model;
using namespace portapack;
#include "cpld_update.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "ui_font_fixed_8x16.hpp"
namespace ui {
@ -169,28 +172,6 @@ void AntennaBiasSetupView::focus() {
button_done.focus();
}
AboutView::AboutView(NavigationView& nav) {
add_children({ {
&text_title,
&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() {
button_ok.focus();
}
@ -464,7 +445,7 @@ SetupMenuView::SetupMenuView(NavigationView& nav) {
{ "Date/Time", ui::Color::white(), [&nav](){ nav.push<SetDateTimeView>(); } },
{ "Frequency correction", ui::Color::white(), [&nav](){ nav.push<SetFrequencyCorrectionView>(); } },
{ "Antenna Bias Voltage", ui::Color::white(), [&nav](){ nav.push<AntennaBiasSetupView>(); } },
{ "Touch screen", ui::Color::white(), [&nav](){ nav.push<SetTouchCalibView>(); } },
{ "Touch screen", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } },
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<SetPlayDeadView>(); } }
} });
on_left = [&nav](){ nav.pop(); };

View File

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

View File

@ -32,6 +32,7 @@
#include "hackrf_gpio.hpp"
#include "portapack.hpp"
#include "radio.hpp"
#include "baseband_api.hpp"
#include "hackrf_hal.hpp"
#include "portapack_shared_memory.hpp"
@ -117,11 +118,8 @@ void XylosView::focus() {
}
XylosView::~XylosView() {
transmitter_model.disable();
}
void XylosView::paint(Painter& painter) {
(void)painter;
receiver_model.disable();
baseband::shutdown();
}
void XylosView::upd_message() {
@ -187,23 +185,98 @@ void XylosView::upd_message() {
void XylosView::start_tx() {
upd_message();
audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
shared_memory.transmit_done = false;
memcpy(shared_memory.xylosdata, ccirmessage, 21);
transmitter_model.enable();
}
XylosView::XylosView(
NavigationView& nav
)
{
int c;
//audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
transmitter_model.set_baseband_configuration({
.mode = 2,
.sampling_rate = 1536000,
.mode = 0,
.sampling_rate = 1536000U,
.decimation_factor = 1,
});
transmitter_model.set_rf_amp(true);
transmitter_model.set_baseband_bandwidth(1750000);
transmitter_model.enable();
baseband::set_xylos_data(ccirmessage);
}
void XylosView::on_txdone(const int n) {
int c, sr;
if (testing == true) {
if (n == 25) {
transmitter_model.disable();
if (sequence_idx != 9) {
chThdSleepMilliseconds(15000);
memcpy(ccirmessage, &xylos_sequence[sequence_idx][0], 21);
// ASCII to frequency LUT index
for (c=0; c<20; c++) {
if (ccirmessage[c] > '9')
ccirmessage[c] -= 0x37;
else
ccirmessage[c] -= 0x30;
}
ccirmessage[20] = 0xFF;
//memcpy(shared_memory.xylosdata, ccirmessage, 21); TODO !!!
sequence_idx++;
txing = true;
transmitter_model.enable();
} else {
button_txtest.set_style(&style());
button_txtest.set_text("TEST");
testing = false;
}
}
} else {
if (n == 25) {
audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
transmitter_model.disable();
progress.set_value(0);
if (inc_cnt) {
chThdSleepMilliseconds(1000);
header_code_b.set_value(header_code_b.value() + 1);
upd_message();
start_tx();
inc_cnt--;
} else {
header_code_b.set_value(header_init);
if (checkbox_cligno.value() == false) {
txing = false;
button_transmit.set_style(&style_val);
button_transmit.set_text("START");
} else {
if (checkbox_hinc.value())
inc_cnt = 3;
else
inc_cnt = 0;
chThdSleepMilliseconds(tempo_cligno.value() * 1000);
// Invert relay states
sr = options_ra.selected_index();
if (sr > 0) options_ra.set_selected_index(sr ^ 3);
sr = options_rb.selected_index();
if (sr > 0) options_rb.set_selected_index(sr ^ 3);
sr = options_rc.selected_index();
if (sr > 0) options_rc.set_selected_index(sr ^ 3);
sr = options_rd.selected_index();
if (sr > 0) options_rd.set_selected_index(sr ^ 3);
upd_message();
start_tx();
}
}
} else {
progress.set_value((n + 1) * 5);
}
}
}
XylosView::XylosView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_xylos);
add_children({ {
&checkbox_hinc,
@ -366,38 +439,6 @@ XylosView::XylosView(
if (txing == false) {
sequence_idx = 0;
EventDispatcher::message_map().unregister_handler(Message::ID::TXDone);
EventDispatcher::message_map().register_handler(Message::ID::TXDone,
[this](Message* const p) {
int c;
const auto message = static_cast<const TXDoneMessage*>(p);
if (message->n == 25) {
transmitter_model.disable();
if (sequence_idx != 9) {
chThdSleepMilliseconds(15000);
memcpy(ccirmessage, &xylos_sequence[sequence_idx][0], 21);
// ASCII to frequency LUT index
for (c=0; c<20; c++) {
if (ccirmessage[c] > '9')
ccirmessage[c] -= 0x37;
else
ccirmessage[c] -= 0x30;
}
ccirmessage[20] = 0xFF;
shared_memory.transmit_done = false;
memcpy(shared_memory.xylosdata, ccirmessage, 21);
sequence_idx++;
txing = true;
transmitter_model.enable();
} else {
button_txtest.set_style(&style());
button_txtest.set_text("TEST");
}
}
}
);
memcpy(ccirmessage, &xylos_sequence[sequence_idx][0], 21);
// ASCII to frequency LUT index
@ -408,14 +449,14 @@ XylosView::XylosView(
ccirmessage[c] -= 0x30;
}
ccirmessage[20] = 0xFF;
shared_memory.transmit_done = false;
memcpy(shared_memory.xylosdata, ccirmessage, 21);
//memcpy(shared_memory.xylosdata, ccirmessage, 21); TODO !!!
sequence_idx++;
transmitter_model.set_tuning_frequency(xylos_freqs[options_freq.selected_index()]);
txing = true;
testing = true;
button_txtest.set_style(&style_cancel);
button_txtest.set_text("Wait");
transmitter_model.enable();
@ -433,70 +474,18 @@ XylosView::XylosView(
inc_cnt = 0;
header_init = header_code_b.value();
EventDispatcher::message_map().unregister_handler(Message::ID::TXDone);
EventDispatcher::message_map().register_handler(Message::ID::TXDone,
[this](Message* const p) {
uint8_t sr;
const auto message = static_cast<const TXDoneMessage*>(p);
if (message->n == 25) {
audio::headphone::set_volume(volume_t::decibel(0 - 99) + audio::headphone::volume_range().max);
transmitter_model.disable();
progress.set_value(0);
if (inc_cnt) {
chThdSleepMilliseconds(1000);
header_code_b.set_value(header_code_b.value() + 1);
upd_message();
start_tx();
inc_cnt--;
} else {
header_code_b.set_value(header_init);
if (checkbox_cligno.value() == false) {
txing = false;
button_transmit.set_style(&style_val);
button_transmit.set_text("START");
} else {
if (checkbox_hinc.value())
inc_cnt = 3;
else
inc_cnt = 0;
chThdSleepMilliseconds(tempo_cligno.value() * 1000);
// Invert relay states
sr = options_ra.selected_index();
if (sr > 0) options_ra.set_selected_index(sr ^ 3);
sr = options_rb.selected_index();
if (sr > 0) options_rb.set_selected_index(sr ^ 3);
sr = options_rc.selected_index();
if (sr > 0) options_rc.set_selected_index(sr ^ 3);
sr = options_rd.selected_index();
if (sr > 0) options_rd.set_selected_index(sr ^ 3);
upd_message();
start_tx();
}
}
} else {
progress.set_value((message->n + 1) * 5);
}
}
);
shared_memory.transmit_done = false;
memcpy(shared_memory.xylosdata, ccirmessage, 21);
//shared_memory.transmit_done = false;
//memcpy(shared_memory.xylosdata, ccirmessage, 21);
transmitter_model.set_tuning_frequency(xylos_freqs[options_freq.selected_index()]);
audio::headphone::set_volume(volume_t::decibel(90 - 99) + audio::headphone::volume_range().max);
txing = true;
testing = false;
button_transmit.set_style(&style_cancel);
button_transmit.set_text("Wait");
transmitter_model.enable();
start_tx();
}
};
}

View File

@ -148,12 +148,12 @@ public:
std::string title() const override { return "Xylos transmit"; };
void focus() override;
void paint(Painter& painter) override;
private:
int inc_cnt;
int header_init;
bool txing = false;
bool testing = false;
const rf::Frequency xylos_freqs[7] = { 31325000, 31387500, 31437500, 31475000, 31687500, 31975000, 88000000 };
char ccirmessage[21];
@ -173,6 +173,7 @@ private:
void start_tx();
void upd_message();
void on_txdone(const int n);
const Style style_val {
.font = font::fixed_8x16,
@ -363,6 +364,14 @@ private:
{ 20 * 8, 16 * 16, 64, 32 },
"TEST"
};
MessageHandlerRegistration message_handler_tx_done {
Message::ID::TXDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXDoneMessage*>(p);
this->on_txdone(message.n);
}
};
};
} /* namespace ui */

View File

@ -1,288 +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.
#
##############################################################################
# Build global options
# NOTE: Can be overridden externally.
#
# 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
# C specific options here (added to USE_OPT).
ifeq ($(USE_COPT),)
USE_COPT = -std=gnu99
endif
# C++ specific options here (added to USE_OPT).
ifeq ($(USE_CPPOPT),)
USE_CPPOPT = -std=c++11 -fno-rtti -fno-exceptions
endif
# Enable this if you want the linker to remove unused code and data
ifeq ($(USE_LINK_GC),)
USE_LINK_GC = yes
endif
# Linker extra options here.
ifeq ($(USE_LDOPT),)
USE_LDOPT =
endif
# Enable this if you want link time optimizations (LTO)
ifeq ($(USE_LTO),)
USE_LTO = no
endif
# If enabled, this option allows to compile the application in THUMB mode.
ifeq ($(USE_THUMB),)
USE_THUMB = yes
endif
# Enable this if you want to see the full log while compiling.
ifeq ($(USE_VERBOSE_COMPILE),)
USE_VERBOSE_COMPILE = no
endif
#
# Build global options
##############################################################################
##############################################################################
# Architecture or project specific options
#
# Enables the use of FPU on Cortex-M4 (no, softfp, hard).
ifeq ($(USE_FPU),)
USE_FPU = hard
endif
#
# Architecture or project specific options
##############################################################################
##############################################################################
# Project, sources and paths
#
# Define project name here
PROJECT = baseband-tx
# 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
# C sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
CSRC = $(PORTSRC) \
$(KERNSRC) \
$(TESTSRC) \
$(HALSRC) \
$(PLATFORMSRC) \
$(BOARDSRC)
# 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_jammer.cpp \
proc_audiotx.cpp \
proc_fsk_lcr.cpp \
proc_epar.cpp \
proc_playaudio.cpp \
proc_xylos.cpp \
proc_rds.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
# 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 =
# 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 =
# 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 =
# 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 =
# List ASM source files here
ASMSRC = $(PORTASM)
INCDIR = ../common $(PORTINC) $(KERNINC) $(TESTINC) \
$(HALINC) $(PLATFORMINC) $(BOARDINC) \
$(CHIBIOS)/os/various
#
# Project, sources and paths
##############################################################################
##############################################################################
# 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
# ARM-specific options here
AOPT =
# THUMB-specific options here
TOPT = -mthumb -DTHUMB
# Define C warning options here
CWARN = -Wall -Wextra -Wstrict-prototypes
# Define C++ warning options here
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
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
DADEFS =
# List all default directories to look for include files here
DINCDIR =
# List the default directory to look for the libraries here
DLIBDIR =
# List all default libraries here
DLIBS =
#
# End of default section
##############################################################################
##############################################################################
# Start of user section
#
# List all user C define here, like -D_DEBUG=1
UDEFS =
# Define ASM defines here
UADEFS =
# List all user directories here
UINCDIR =
# List the user directory to look for the libraries here
ULIBDIR =
# List all user libraries here
ULIBS =
#
# End of user defines
##############################################################################
RULESPATH = $(CHIBIOS)/os/ports/GCC/ARMCMx
include $(RULESPATH)/rules.mk

View File

@ -1,52 +0,0 @@
/*
* 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 "audio_compressor.hpp"
float GainComputer::operator()(const float x) const {
const auto abs_x = std::abs(x);
const auto db = (abs_x < lin_floor) ? db_floor : log2_db_k * fast_log2(abs_x);
const auto overshoot_db = db - threshold_db;
if( knee_width_db > 0.0f ) {
const auto w2 = knee_width_db / 2.0f;
const auto a = w2 / (knee_width_db * knee_width_db);
const auto in_transition = (overshoot_db > -w2) && (overshoot_db < w2);
const auto rectified_overshoot = in_transition ? (a * std::pow(overshoot_db + w2, 2.0f)) : std::max(overshoot_db, 0.0f);
return rectified_overshoot * slope;
} else {
const auto rectified_overshoot = std::max(overshoot_db, 0.0f);
return rectified_overshoot * slope;
}
}
void FeedForwardCompressor::execute_in_place(const buffer_f32_t& buffer) {
constexpr float makeup_gain = std::pow(10.0f, (threshold - (threshold / ratio)) / -20.0f);
for(size_t i=0; i<buffer.count; i++) {
buffer.p[i] = execute_once(buffer.p[i]) * makeup_gain;
}
}
float FeedForwardCompressor::execute_once(const float x) {
const auto gain_db = gain_computer(x);
const auto peak_db = -peak_detector(-gain_db);
const auto gain = fast_pow2(peak_db * (3.321928094887362f / 20.0f));
return x * gain;
}

View File

@ -1,102 +0,0 @@
/*
* 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.
*/
#ifndef __AUDIO_COMPRESSOR_H__
#define __AUDIO_COMPRESSOR_H__
#include "dsp_types.hpp"
#include "utility.hpp"
#include <cmath>
/* Code based on article in Journal of the Audio Engineering Society
* Vol. 60, No. 6, 2012 June, by Dimitrios Giannoulis, Michael Massberg,
* Joshua D. Reiss "Digital Dynamic Range Compressor Design A Tutorial
* and Analysis"
*/
class GainComputer {
public:
constexpr GainComputer(
float ratio,
float threshold
) : ratio { ratio },
slope { 1.0f / ratio - 1.0f },
threshold_db { threshold }
{
}
float operator()(const float x) const;
private:
const float ratio;
const float slope;
const float threshold_db;
static constexpr float knee_width_db = 0.0f;
static constexpr float db_floor = -120.0f;
static constexpr float lin_floor = std::pow(10.0f, db_floor / 20.0f);
static constexpr float log2_db_k = 20.0f * std::log10(2.0f);
};
class PeakDetectorBranchingSmooth {
public:
constexpr PeakDetectorBranchingSmooth(
float att_a,
float rel_a
) : att_a { att_a },
rel_a { rel_a }
{
}
float operator()(const float db) {
const auto a = (db > state) ? att_a : rel_a;
state = db + a * (state - db);
return state;
}
private:
float state { 0.0f };
const float att_a;
const float rel_a;
};
class FeedForwardCompressor {
public:
void execute_in_place(const buffer_f32_t& buffer);
private:
static constexpr float fs = 12000.0f;
static constexpr float ratio = 10.0f;
static constexpr float threshold = -30.0f;
GainComputer gain_computer { ratio, threshold };
PeakDetectorBranchingSmooth peak_detector { tau_alpha(0.010f, fs), tau_alpha(0.300f, fs) };
float execute_once(const float x);
static constexpr float tau_alpha(const float tau, const float fs) {
return std::exp(-1.0f / (tau * fs));
}
};
#endif/*__AUDIO_COMPRESSOR_H__*/

View File

@ -1,238 +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.
*/
#include "audio_dma.hpp"
#include <cstdint>
#include <cstddef>
#include <array>
#include "hal.h"
#include "gpdma.hpp"
using namespace lpc43xx;
#include "portapack_dma.hpp"
namespace audio {
namespace dma {
constexpr uint32_t gpdma_ahb_master_peripheral = 1;
constexpr uint32_t gpdma_ahb_master_memory = 0;
constexpr uint32_t gpdma_ahb_master_lli_fetch = 0;
constexpr uint32_t gpdma_rx_peripheral = 0x9; /* I2S0 DMA request 1 */
constexpr uint32_t gpdma_rx_src_peripheral = gpdma_rx_peripheral;
constexpr uint32_t gpdma_rx_dest_peripheral = gpdma_rx_peripheral;
constexpr uint32_t gpdma_tx_peripheral = 0xa; /* I2S0 DMA request 2 */
constexpr uint32_t gpdma_tx_src_peripheral = gpdma_tx_peripheral;
constexpr uint32_t gpdma_tx_dest_peripheral = gpdma_tx_peripheral;
constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) {
return {
.lm = gpdma_ahb_master_lli_fetch,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
}
constexpr gpdma::channel::Control control_tx(const size_t transfer_bytes) {
return {
.transfersize = gpdma::buffer_words(transfer_bytes, 4),
.sbsize = 4, /* Burst size: 32 */
.dbsize = 4, /* Burst size: 32 */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = gpdma_ahb_master_memory,
.d = gpdma_ahb_master_peripheral,
.si = 1,
.di = 0,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
}
constexpr gpdma::channel::Config config_tx() {
return {
.e = 0,
.srcperipheral = gpdma_tx_src_peripheral,
.destperipheral = gpdma_tx_dest_peripheral,
.flowcntrl = gpdma::FlowControl::MemoryToPeripheral_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
}
constexpr gpdma::channel::Control control_rx(const size_t transfer_bytes) {
return {
.transfersize = gpdma::buffer_words(transfer_bytes, 4),
.sbsize = 4, /* Burst size: 32 */
.dbsize = 4, /* Burst size: 32 */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = gpdma_ahb_master_peripheral,
.d = gpdma_ahb_master_memory,
.si = 0,
.di = 1,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
}
constexpr gpdma::channel::Config config_rx() {
return {
.e = 0,
.srcperipheral = gpdma_rx_src_peripheral,
.destperipheral = gpdma_rx_dest_peripheral,
.flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
}
/* TODO: Clean up terminology around "buffer", "transfer", "samples" */
constexpr size_t buffer_samples_log2n = 7;
constexpr size_t buffer_samples = (1 << buffer_samples_log2n);
constexpr size_t transfers_per_buffer_log2n = 2;
constexpr size_t transfers_per_buffer = (1 << transfers_per_buffer_log2n);
constexpr size_t transfer_samples = buffer_samples / transfers_per_buffer;
constexpr size_t transfers_mask = transfers_per_buffer - 1;
constexpr size_t buffer_bytes = buffer_samples * sizeof(sample_t);
constexpr size_t transfer_bytes = transfer_samples * sizeof(sample_t);
static std::array<sample_t, buffer_samples> buffer_tx;
static std::array<sample_t, buffer_samples> buffer_rx;
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_tx_loop;
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_rx_loop;
static constexpr auto& gpdma_channel_i2s0_tx = gpdma::channels[portapack::i2s0_tx_gpdma_channel_number];
static constexpr auto& gpdma_channel_i2s0_rx = gpdma::channels[portapack::i2s0_rx_gpdma_channel_number];
static volatile const gpdma::channel::LLI* tx_next_lli = nullptr;
static volatile const gpdma::channel::LLI* rx_next_lli = nullptr;
static void tx_transfer_complete() {
tx_next_lli = gpdma_channel_i2s0_tx.next_lli();
}
static void tx_error() {
disable();
}
static void rx_transfer_complete() {
rx_next_lli = gpdma_channel_i2s0_rx.next_lli();
}
static void rx_error() {
disable();
}
void init() {
gpdma_channel_i2s0_tx.set_handlers(tx_transfer_complete, tx_error);
gpdma_channel_i2s0_rx.set_handlers(rx_transfer_complete, rx_error);
// LPC_GPDMA->SYNC |= (1 << gpdma_rx_peripheral);
// LPC_GPDMA->SYNC |= (1 << gpdma_tx_peripheral);
}
static void configure_tx() {
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->TXFIFO);
const auto control_value = control_tx(transfer_bytes);
for(size_t i=0; i<lli_tx_loop.size(); i++) {
const auto memory = reinterpret_cast<uint32_t>(&buffer_tx[i * transfer_samples]);
lli_tx_loop[i].srcaddr = memory;
lli_tx_loop[i].destaddr = peripheral;
lli_tx_loop[i].lli = lli_pointer(&lli_tx_loop[(i + 1) % lli_tx_loop.size()]);
lli_tx_loop[i].control = control_value;
}
}
static void configure_rx() {
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->RXFIFO);
const auto control_value = control_rx(transfer_bytes);
for(size_t i=0; i<lli_rx_loop.size(); i++) {
const auto memory = reinterpret_cast<uint32_t>(&buffer_rx[i * transfer_samples]);
lli_rx_loop[i].srcaddr = peripheral;
lli_rx_loop[i].destaddr = memory;
lli_rx_loop[i].lli = lli_pointer(&lli_rx_loop[(i + 1) % lli_rx_loop.size()]);
lli_rx_loop[i].control = control_value;
}
}
void configure() {
configure_tx();
configure_rx();
}
void enable() {
const auto gpdma_config_tx = config_tx();
const auto gpdma_config_rx = config_rx();
gpdma_channel_i2s0_tx.configure(lli_tx_loop[0], gpdma_config_tx);
gpdma_channel_i2s0_rx.configure(lli_rx_loop[0], gpdma_config_rx);
gpdma_channel_i2s0_tx.enable();
gpdma_channel_i2s0_rx.enable();
}
void disable() {
gpdma_channel_i2s0_tx.disable();
gpdma_channel_i2s0_rx.disable();
}
buffer_t tx_empty_buffer() {
const auto next_lli = tx_next_lli;
if( next_lli ) {
const size_t next_index = next_lli - &lli_tx_loop[0];
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return { reinterpret_cast<sample_t*>(lli_tx_loop[free_index].srcaddr), transfer_samples };
} else {
return { nullptr, 0 };
}
}
buffer_t rx_empty_buffer() {
const auto next_lli = rx_next_lli;
if( next_lli ) {
const size_t next_index = next_lli - &lli_rx_loop[0];
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return { reinterpret_cast<sample_t*>(lli_rx_loop[free_index].srcaddr), transfer_samples };
} else {
return { nullptr, 0 };
}
}
} /* namespace dma */
} /* namespace audio */

View File

@ -1,56 +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.
*/
#ifndef __AUDIO_DMA_H__
#define __AUDIO_DMA_H__
#include <cstdint>
#include "buffer.hpp"
namespace audio {
struct sample_t {
union {
struct {
int16_t left;
int16_t right;
};
uint32_t raw;
};
};
using buffer_t = buffer_t<sample_t>;
namespace dma {
void init();
void configure();
void enable();
void disable();
audio::buffer_t tx_empty_buffer();
audio::buffer_t rx_empty_buffer();
} /* namespace dma */
} /* namespace audio */
#endif/*__AUDIO_DMA_H__*/

View File

@ -1,114 +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.
*/
#include "audio_output.hpp"
#include "portapack_shared_memory.hpp"
#include "audio_dma.hpp"
#include "message.hpp"
#include <cstdint>
#include <cstddef>
#include <array>
void AudioOutput::configure(
const iir_biquad_config_t& hpf_config,
const iir_biquad_config_t& deemph_config,
const float squelch_threshold
) {
hpf.configure(hpf_config);
deemph.configure(deemph_config);
squelch.set_threshold(squelch_threshold);
}
void AudioOutput::write(
const buffer_s16_t& audio
) {
std::array<float, 32> audio_f;
for(size_t i=0; i<audio.count; i++) {
audio_f[i] = audio.p[i] * ki;
}
write(buffer_f32_t {
audio_f.data(),
audio.count,
audio.sampling_rate
});
}
void AudioOutput::write(
const buffer_f32_t& audio
) {
block_buffer.feed(
audio,
[this](const buffer_f32_t& buffer) {
this->on_block(buffer);
}
);
}
void AudioOutput::on_block(
const buffer_f32_t& audio
) {
const auto audio_present_now = squelch.execute(audio);
hpf.execute_in_place(audio);
deemph.execute_in_place(audio);
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
const bool audio_present = (audio_present_history != 0);
if( !audio_present ) {
for(size_t i=0; i<audio.count; i++) {
audio.p[i] = 0;
}
}
fill_audio_buffer(audio, audio_present);
}
void AudioOutput::fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo) {
std::array<int16_t, 32> audio_int;
auto audio_buffer = audio::dma::tx_empty_buffer();
for(size_t i=0; i<audio_buffer.count; i++) {
const int32_t sample_int = audio.p[i] * k;
const int32_t sample_saturated = __SSAT(sample_int, 16);
audio_buffer.p[i].left = audio_buffer.p[i].right = sample_saturated;
audio_int[i] = sample_saturated;
}
if( stream && send_to_fifo ) {
stream->write(audio_int.data(), audio_buffer.count * sizeof(audio_int[0]));
}
feed_audio_stats(audio);
}
void AudioOutput::feed_audio_stats(const buffer_f32_t& audio) {
audio_stats.feed(
audio,
[](const AudioStatistics& statistics) {
const AudioStatisticsMessage audio_stats_message { statistics };
shared_memory.application_queue.push(audio_stats_message);
}
);
}

View File

@ -1,73 +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.
*/
#ifndef __AUDIO_OUTPUT_H__
#define __AUDIO_OUTPUT_H__
#include "dsp_types.hpp"
#include "dsp_iir.hpp"
#include "dsp_squelch.hpp"
#include "stream_input.hpp"
#include "block_decimator.hpp"
#include "audio_stats_collector.hpp"
#include <cstdint>
#include <memory>
class AudioOutput {
public:
void configure(
const iir_biquad_config_t& hpf_config,
const iir_biquad_config_t& deemph_config = iir_config_passthrough,
const float squelch_threshold = 0.0f
);
void write(const buffer_s16_t& audio);
void write(const buffer_f32_t& audio);
void set_stream(std::unique_ptr<StreamInput> new_stream) {
stream = std::move(new_stream);
}
private:
static constexpr float k = 32768.0f;
static constexpr float ki = 1.0f / k;
BlockDecimator<float, 32> block_buffer { 1 };
IIRBiquadFilter hpf;
IIRBiquadFilter deemph;
FMSquelch squelch;
std::unique_ptr<StreamInput> stream;
AudioStatsCollector audio_stats;
uint64_t audio_present_history = 0;
void on_block(const buffer_f32_t& audio);
void fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo);
void feed_audio_stats(const buffer_f32_t& audio);
};
#endif/*__AUDIO_OUTPUT_H__*/

View File

@ -1,67 +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.
*/
#include "audio_stats_collector.hpp"
#include "utility.hpp"
void AudioStatsCollector::consume_audio_buffer(const buffer_f32_t& src) {
auto src_p = src.p;
const auto src_end = &src.p[src.count];
while(src_p < src_end) {
const auto sample = *(src_p++);
const auto sample_squared = sample * sample;
squared_sum += sample_squared;
if( sample_squared > max_squared ) {
max_squared = sample_squared;
}
}
}
bool AudioStatsCollector::update_stats(const size_t sample_count, const size_t sampling_rate) {
count += sample_count;
const size_t samples_per_update = sampling_rate * update_interval;
if( count >= samples_per_update ) {
statistics.rms_db = mag2_to_dbv_norm(squared_sum / count);
statistics.max_db = mag2_to_dbv_norm(max_squared);
statistics.count = count;
squared_sum = 0;
max_squared = 0;
count = 0;
return true;
} else {
return false;
}
}
bool AudioStatsCollector::feed(const buffer_f32_t& src) {
consume_audio_buffer(src);
return update_stats(src.count, src.sampling_rate);
}
bool AudioStatsCollector::mute(const size_t sample_count, const size_t sampling_rate) {
return update_stats(sample_count, sampling_rate);
}

View File

@ -1,63 +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.
*/
#ifndef __AUDIO_STATS_COLLECTOR_H__
#define __AUDIO_STATS_COLLECTOR_H__
#include "dsp_types.hpp"
#include "message.hpp"
#include <cstdint>
#include <cstddef>
class AudioStatsCollector {
public:
template<typename Callback>
void feed(const buffer_f32_t& src, Callback callback) {
if( feed(src) ) {
callback(statistics);
}
}
template<typename Callback>
void mute(const size_t sample_count, const size_t sampling_rate, Callback callback) {
if( mute(sample_count, sampling_rate) ) {
callback(statistics);
}
}
private:
static constexpr float update_interval { 0.1f };
float squared_sum { 0 };
float max_squared { 0 };
size_t count { 0 };
AudioStatistics statistics;
void consume_audio_buffer(const buffer_f32_t& src);
bool update_stats(const size_t sample_count, const size_t sampling_rate);
bool feed(const buffer_f32_t& src);
bool mute(const size_t sample_count, const size_t sampling_rate);
};
#endif/*__AUDIO_STATS_COLLECTOR_H__*/

View File

@ -1,175 +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.
*/
#include "baseband_dma.hpp"
#include <cstdint>
#include <cstddef>
#include <array>
#include "hal.h"
#include "gpdma.hpp"
using namespace lpc43xx;
#include "portapack_dma.hpp"
#include "thread_wait.hpp"
namespace baseband {
namespace dma {
constexpr uint32_t gpdma_ahb_master_sgpio = 0;
constexpr uint32_t gpdma_ahb_master_memory = 1;
constexpr uint32_t gpdma_ahb_master_lli_fetch = 0;
constexpr uint32_t gpdma_src_peripheral = 0x0;
constexpr uint32_t gpdma_dest_peripheral = 0x0;
constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) {
return {
.lm = gpdma_ahb_master_lli_fetch,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
}
constexpr gpdma::channel::Control control(const baseband::Direction direction, const size_t buffer_words) {
return {
.transfersize = buffer_words,
.sbsize = 0, /* Burst size: 1 */
.dbsize = 0, /* Burst size: 1 */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_memory : gpdma_ahb_master_sgpio,
.d = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_sgpio : gpdma_ahb_master_memory,
.si = (direction == baseband::Direction::Transmit) ? 1U : 0U,
.di = (direction == baseband::Direction::Transmit) ? 0U : 1U,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
}
constexpr gpdma::channel::Config config(const baseband::Direction direction) {
return {
.e = 0,
.srcperipheral = gpdma_src_peripheral,
.destperipheral = gpdma_dest_peripheral,
.flowcntrl = (direction == baseband::Direction::Transmit)
? gpdma::FlowControl::MemoryToPeripheral_DMAControl
: gpdma::FlowControl::PeripheralToMemory_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
}
constexpr size_t buffer_samples_log2n = 13;
constexpr size_t buffer_samples = (1 << buffer_samples_log2n);
constexpr size_t transfers_per_buffer_log2n = 2;
constexpr size_t transfers_per_buffer = (1 << transfers_per_buffer_log2n);
constexpr size_t transfer_samples = buffer_samples / transfers_per_buffer;
constexpr size_t transfers_mask = transfers_per_buffer - 1;
constexpr size_t buffer_bytes = buffer_samples * sizeof(baseband::sample_t);
constexpr size_t transfer_bytes = transfer_samples * sizeof(baseband::sample_t);
constexpr size_t msg_count = transfers_per_buffer - 1;
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_loop;
static constexpr auto& gpdma_channel_sgpio = gpdma::channels[portapack::sgpio_gpdma_channel_number];
static ThreadWait thread_wait;
static void transfer_complete() {
const auto next_lli_index = gpdma_channel_sgpio.next_lli() - &lli_loop[0];
thread_wait.wake_from_interrupt(next_lli_index);
}
static void dma_error() {
thread_wait.wake_from_interrupt(-1);
disable();
}
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);
}
void configure(
baseband::sample_t* const buffer_base,
const baseband::Direction direction
) {
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_SGPIO->REG_SS[0]);
const auto control_value = control(direction, gpdma::buffer_words(transfer_bytes, 4));
for(size_t i=0; i<lli_loop.size(); i++) {
const auto memory = reinterpret_cast<uint32_t>(&buffer_base[i * transfer_samples]);
lli_loop[i].srcaddr = (direction == Direction::Transmit) ? memory : peripheral;
lli_loop[i].destaddr = (direction == Direction::Transmit) ? peripheral : memory;
lli_loop[i].lli = lli_pointer(&lli_loop[(i + 1) % lli_loop.size()]);
lli_loop[i].control = control_value;
}
}
void enable(const baseband::Direction direction) {
const auto gpdma_config = config(direction);
gpdma_channel_sgpio.configure(lli_loop[0], gpdma_config);
gpdma_channel_sgpio.enable();
}
bool is_enabled() {
return gpdma_channel_sgpio.is_enabled();
}
void disable() {
gpdma_channel_sgpio.disable();
}
baseband::buffer_t wait_for_rx_buffer() {
const auto next_index = thread_wait.sleep();
if( next_index >= 0 ) {
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return { reinterpret_cast<sample_t*>(lli_loop[free_index].destaddr), transfer_samples };
} else {
return { };
}
}
baseband::buffer_t wait_for_tx_buffer() {
const auto next_index = thread_wait.sleep();
if( next_index >= 0 ) {
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return { reinterpret_cast<sample_t*>(lli_loop[free_index].srcaddr), transfer_samples };
} else {
return { };
}
}
} /* namespace dma */
} /* namespace baseband */

View File

@ -1,53 +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.
*/
#ifndef __BASEBAND_DMA_H__
#define __BASEBAND_DMA_H__
#include <cstddef>
#include <array>
#include "complex.hpp"
#include "baseband.hpp"
namespace baseband {
namespace dma {
using Handler = void (*)();
void init();
void configure(
baseband::sample_t* const buffer_base,
const baseband::Direction direction
);
void enable(const baseband::Direction direction);
bool is_enabled();
void disable();
baseband::buffer_t wait_for_rx_buffer();
baseband::buffer_t wait_for_tx_buffer();
} /* namespace dma */
} /* namespace baseband */
#endif/*__BASEBAND_DMA_H__*/

View File

@ -1,36 +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.
*/
#include "baseband_processor.hpp"
#include "portapack_shared_memory.hpp"
#include "message.hpp"
void BasebandProcessor::feed_channel_stats(const buffer_c16_t& channel) {
channel_stats.feed(
channel,
[](const ChannelStatistics& statistics) {
const ChannelStatisticsMessage channel_stats_message { statistics };
shared_memory.application_queue.push(channel_stats_message);
}
);
}

View File

@ -1,46 +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.
*/
#ifndef __BASEBAND_PROCESSOR_H__
#define __BASEBAND_PROCESSOR_H__
#include "dsp_types.hpp"
#include "channel_stats_collector.hpp"
#include "message.hpp"
class BasebandProcessor {
public:
virtual ~BasebandProcessor() = default;
virtual void execute(const buffer_c8_t& buffer) = 0;
virtual void on_message(const Message* const) { };
protected:
void feed_channel_stats(const buffer_c16_t& channel);
private:
ChannelStatsCollector channel_stats;
};
#endif/*__BASEBAND_PROCESSOR_H__*/

View File

@ -1,59 +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.
*/
#include "baseband_stats_collector.hpp"
#include "lpc43xx_cpp.hpp"
bool BasebandStatsCollector::process(const buffer_c8_t& buffer) {
samples += buffer.count;
const size_t report_samples = buffer.sampling_rate * report_interval;
const auto report_delta = samples - samples_last_report;
return report_delta >= report_samples;
}
BasebandStatistics BasebandStatsCollector::capture_statistics() {
BasebandStatistics statistics;
const auto idle_ticks = thread_idle->total_ticks;
statistics.idle_ticks = (idle_ticks - last_idle_ticks);
last_idle_ticks = idle_ticks;
const auto main_ticks = thread_main->total_ticks;
statistics.main_ticks = (main_ticks - last_main_ticks);
last_main_ticks = main_ticks;
const auto rssi_ticks = thread_rssi->total_ticks;
statistics.rssi_ticks = (rssi_ticks - last_rssi_ticks);
last_rssi_ticks = rssi_ticks;
const auto baseband_ticks = thread_baseband->total_ticks;
statistics.baseband_ticks = (baseband_ticks - last_baseband_ticks);
last_baseband_ticks = baseband_ticks;
statistics.saturation = lpc43xx::m4::flag_saturation();
lpc43xx::m4::clear_flag_saturation();
samples_last_report = samples;
return statistics;
}

View File

@ -1,71 +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.
*/
#ifndef __BASEBAND_STATS_COLLECTOR_H__
#define __BASEBAND_STATS_COLLECTOR_H__
#include "ch.h"
#include "dsp_types.hpp"
#include "message.hpp"
#include <cstdint>
#include <cstddef>
class BasebandStatsCollector {
public:
BasebandStatsCollector(
const Thread* const thread_idle,
const Thread* const thread_main,
const Thread* const thread_rssi,
const Thread* const thread_baseband
) : thread_idle { thread_idle },
thread_main { thread_main },
thread_rssi { thread_rssi },
thread_baseband { thread_baseband }
{
}
template<typename Callback>
void process(const buffer_c8_t& buffer, Callback callback) {
if( process(buffer) ) {
callback(capture_statistics());
}
}
private:
static constexpr float report_interval { 1.0f };
size_t samples { 0 };
size_t samples_last_report { 0 };
const Thread* const thread_idle;
uint32_t last_idle_ticks { 0 };
const Thread* const thread_main;
uint32_t last_main_ticks { 0 };
const Thread* const thread_rssi;
uint32_t last_rssi_ticks { 0 };
const Thread* const thread_baseband;
uint32_t last_baseband_ticks { 0 };
bool process(const buffer_c8_t& buffer);
BasebandStatistics capture_statistics();
};
#endif/*__BASEBAND_STATS_COLLECTOR_H__*/

View File

@ -1,152 +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.
*/
#include "baseband_thread.hpp"
#include "dsp_types.hpp"
#include "baseband.hpp"
#include "baseband_stats_collector.hpp"
#include "baseband_sgpio.hpp"
#include "baseband_dma.hpp"
#include "proc_playaudio.hpp"
#include "proc_audiotx.hpp"
#include "proc_xylos.hpp"
#include "proc_epar.hpp"
#include "proc_fsk_lcr.hpp"
#include "proc_rds.hpp"
#include "proc_jammer.hpp"
#include "rssi.hpp"
#include "i2s.hpp"
#include "portapack_shared_memory.hpp"
#include <array>
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),
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);
}
}
}
void BasebandThread::run() {
baseband_sgpio.init();
baseband::dma::init();
const auto baseband_buffer = std::make_unique<std::array<baseband::sample_t, 8192>>();
baseband::dma::configure(
baseband_buffer->data(),
baseband::Direction::Transmit
);
//baseband::dma::allocate(4, 2048);
BasebandStatsCollector stats {
chSysGetIdleThread(),
thread_main,
thread_rssi,
chThdSelf()
};
while(true) {
// TODO: Place correct sampling rate into buffer returned here:
const auto buffer_tmp = baseband::dma::wait_for_tx_buffer();
if( buffer_tmp ) {
buffer_c8_t buffer {
buffer_tmp.p, buffer_tmp.count, baseband_configuration.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);
}
);
}
}
}
BasebandProcessor* BasebandThread::create_processor(const int32_t mode) {
switch(mode) {
case 0: return new PlayAudioProcessor();
case 1: return new AudioTXProcessor();
case 2: return new XylosProcessor();
case 3: return new LCRFSKProcessor();
case 4: return new EPARProcessor();
case 5: return new RDSProcessor();
case 6: return new JammerProcessor();
default: return nullptr;
}
}
void BasebandThread::disable() {
if( baseband_processor ) {
i2s::i2s0::tx_mute();
baseband::dma::disable();
baseband_sgpio.streaming_disable();
rf::rssi::stop();
}
}
void BasebandThread::enable() {
if( baseband_processor ) {
baseband_sgpio.configure(baseband::Direction::Transmit);
baseband::dma::enable(baseband::Direction::Transmit);
baseband_sgpio.streaming_enable();
}
}

View File

@ -1,57 +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.
*/
#ifndef __BASEBAND_THREAD_H__
#define __BASEBAND_THREAD_H__
#include "thread_base.hpp"
#include "message.hpp"
#include "baseband_processor.hpp"
#include <ch.h>
class BasebandThread : public ThreadBase {
public:
Thread* start(const tprio_t priority);
void on_message(const Message* const message);
void wait_for_switch(void);
Thread* thread_main { nullptr };
Thread* thread_rssi { nullptr };
private:
BasebandProcessor* baseband_processor { nullptr };
BasebandConfiguration baseband_configuration;
void run() override;
BasebandProcessor* create_processor(const int32_t mode);
void disable();
void enable();
void set_configuration(const BasebandConfiguration& new_configuration);
};
#endif/*__BASEBAND_THREAD_H__*/

View File

@ -1,100 +0,0 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef __BLOCK_DECIMATOR_H__
#define __BLOCK_DECIMATOR_H__
#include <cstdint>
#include <cstddef>
#include <array>
#include "dsp_types.hpp"
#include "complex.hpp"
template<typename T, size_t N>
class BlockDecimator {
public:
constexpr BlockDecimator(
const size_t factor
) : factor_ { factor }
{
}
void set_input_sampling_rate(const uint32_t new_sampling_rate) {
if( new_sampling_rate != input_sampling_rate() ) {
input_sampling_rate_ = new_sampling_rate;
reset_state();
}
}
uint32_t input_sampling_rate() const {
return input_sampling_rate_;
}
void set_factor(const size_t new_factor) {
if( new_factor != factor() ) {
factor_ = new_factor;
reset_state();
}
}
size_t factor() const {
return factor_;
}
uint32_t output_sampling_rate() const {
return input_sampling_rate() / factor();
}
template<typename BlockCallback>
void feed(const buffer_t<T>& src, BlockCallback callback) {
/* NOTE: Input block size must be >= factor */
set_input_sampling_rate(src.sampling_rate);
while( src_i < src.count ) {
buffer[dst_i++] = src.p[src_i];
if( dst_i == buffer.size() ) {
callback({ buffer.data(), buffer.size(), output_sampling_rate() });
reset_state();
dst_i = 0;
}
src_i += factor();
}
src_i -= src.count;
}
private:
std::array<T, N> buffer;
uint32_t input_sampling_rate_ { 0 };
size_t factor_ { 1 };
size_t src_i { 0 };
size_t dst_i { 0 };
void reset_state() {
src_i = 0;
dst_i = 0;
}
};
#endif/*__BLOCK_DECIMATOR_H__*/

View File

@ -1,91 +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.
*/
#include "channel_decimator.hpp"
buffer_c16_t ChannelDecimator::execute_decimation(const buffer_c8_t& buffer) {
const buffer_c16_t work_baseband_buffer {
work_baseband.data(),
work_baseband.size()
};
const buffer_s16_t work_audio_buffer {
(int16_t*)work_baseband.data(),
sizeof(work_baseband) / sizeof(int16_t)
};
/* 3.072MHz complex<int8_t>[2048], [-128, 127]
* -> Shift by -fs/4
* -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs
* -0.1dB @ 86kHz, -1dB @ 270kHz, -60dB @ 1.44MHz
* -> gain of 256
* -> decimation by 2
* -> 1.544MHz complex<int16_t>[1024], [-32768, 32512] */
auto stage_0_out = execute_stage_0(buffer, work_baseband_buffer);
if( decimation_factor == DecimationFactor::By2 ) {
return stage_0_out;
}
/* 1.536MHz complex<int16_t>[1024], [-32768, 32512]
* -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs
* -0.1dB @ 43kHz, -1dB @ 136kHz, -60dB @ 723kHz
* -> gain of 1
* -> decimation by 2
* -> 768kHz complex<int16_t>[512], [-8192, 8128] */
auto cic_1_out = cic_1.execute(stage_0_out, work_baseband_buffer);
if( decimation_factor == DecimationFactor::By4 ) {
return cic_1_out;
}
/* 768kHz complex<int16_t>[512], [-32768, 32512]
* -> 3rd order CIC decimation by 2, gain of 1
* -> 384kHz complex<int16_t>[256], [-32768, 32512] */
auto cic_2_out = cic_2.execute(cic_1_out, work_baseband_buffer);
if( decimation_factor == DecimationFactor::By8 ) {
return cic_2_out;
}
/* 384kHz complex<int16_t>[256], [-32768, 32512]
* -> 3rd order CIC decimation by 2, gain of 1
* -> 192kHz complex<int16_t>[128], [-32768, 32512] */
auto cic_3_out = cic_3.execute(cic_2_out, work_baseband_buffer);
if( decimation_factor == DecimationFactor::By16 ) {
return cic_3_out;
}
/* 192kHz complex<int16_t>[128], [-32768, 32512]
* -> 3rd order CIC decimation by 2, gain of 1
* -> 96kHz complex<int16_t>[64], [-32768, 32512] */
auto cic_4_out = cic_4.execute(cic_3_out, work_baseband_buffer);
return cic_4_out;
}
buffer_c16_t ChannelDecimator::execute_stage_0(
const buffer_c8_t& buffer,
const buffer_c16_t& work_baseband_buffer
) {
if( fs_over_4_downconvert ) {
return translate.execute(buffer, work_baseband_buffer);
} else {
return cic_0.execute(buffer, work_baseband_buffer);
}
}

View File

@ -1,87 +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.
*/
#ifndef __CHANNEL_DECIMATOR_H__
#define __CHANNEL_DECIMATOR_H__
#include "buffer.hpp"
#include "complex.hpp"
#include "dsp_decimate.hpp"
#include <array>
class ChannelDecimator {
public:
enum class DecimationFactor {
By2,
By4,
By8,
By16,
By32,
};
constexpr ChannelDecimator(
) : decimation_factor { DecimationFactor::By32 },
fs_over_4_downconvert { true }
{
}
constexpr ChannelDecimator(
const DecimationFactor decimation_factor,
const bool fs_over_4_downconvert = true
) : decimation_factor { decimation_factor },
fs_over_4_downconvert { fs_over_4_downconvert }
{
}
void set_decimation_factor(const DecimationFactor f) {
decimation_factor = f;
}
buffer_c16_t execute(const buffer_c8_t& buffer) {
auto decimated = execute_decimation(buffer);
return decimated;
}
private:
std::array<complex16_t, 1024> work_baseband;
dsp::decimate::TranslateByFSOver4AndDecimateBy2CIC3 translate;
dsp::decimate::Complex8DecimateBy2CIC3 cic_0;
dsp::decimate::DecimateBy2CIC3 cic_1;
dsp::decimate::DecimateBy2CIC3 cic_2;
dsp::decimate::DecimateBy2CIC3 cic_3;
dsp::decimate::DecimateBy2CIC3 cic_4;
DecimationFactor decimation_factor;
const bool fs_over_4_downconvert;
buffer_c16_t execute_decimation(const buffer_c8_t& buffer);
buffer_c16_t execute_stage_0(
const buffer_c8_t& buffer,
const buffer_c16_t& work_baseband_buffer
);
};
#endif/*__CHANNEL_DECIMATOR_H__*/

View File

@ -1,66 +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.
*/
#ifndef __CHANNEL_STATS_COLLECTOR_H__
#define __CHANNEL_STATS_COLLECTOR_H__
#include "dsp_types.hpp"
#include "message.hpp"
#include "utility.hpp"
#include <cstdint>
#include <cstddef>
#include <hal.h>
class ChannelStatsCollector {
public:
template<typename Callback>
void feed(const buffer_c16_t& src, Callback callback) {
auto src_p = src.p;
while(src_p < &src.p[src.count]) {
const uint32_t sample = *__SIMD32(src_p)++;
const uint32_t mag_sq = __SMUAD(sample, sample);
if( mag_sq > max_squared ) {
max_squared = mag_sq;
}
}
count += src.count;
const size_t samples_per_update = src.sampling_rate * update_interval;
if( count >= samples_per_update ) {
const float max_squared_f = max_squared;
const int32_t max_db = mag2_to_dbv_norm(max_squared_f * (1.0f / (32768.0f * 32768.0f)));
callback({ max_db, count });
max_squared = 0;
count = 0;
}
}
private:
static constexpr float update_interval { 0.1f };
uint32_t max_squared { 0 };
size_t count { 0 };
};
#endif/*__CHANNEL_STATS_COLLECTOR_H__*/

View File

@ -1,546 +0,0 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
Copyright (C) 2014 Jared Boone, ShareBrained Technology
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file templates/chconf.h
* @brief Configuration file template.
* @details A copy of this file must be placed in each project directory, it
* contains the application specific kernel settings.
*
* @addtogroup config
* @details Kernel related settings and hooks.
* @{
*/
#ifndef _CHCONF_H_
#define _CHCONF_H_
/*===========================================================================*/
/**
* @name Kernel parameters and options
* @{
*/
/*===========================================================================*/
/**
* @brief System tick frequency.
* @details Frequency of the system timer that drives the system ticks. This
* setting also defines the system tick time unit.
*/
#if !defined(CH_FREQUENCY) || defined(__DOXYGEN__)
#define CH_FREQUENCY 1000
#endif
/**
* @brief Round robin interval.
* @details This constant is the number of system ticks allowed for the
* threads before preemption occurs. Setting this value to zero
* disables the preemption for threads with equal priority and the
* round robin becomes cooperative. Note that higher priority
* threads can still preempt, the kernel is always preemptive.
*
* @note Disabling the round robin preemption makes the kernel more compact
* and generally faster.
*/
#if !defined(CH_TIME_QUANTUM) || defined(__DOXYGEN__)
#define CH_TIME_QUANTUM 0
#endif
/**
* @brief Managed RAM size.
* @details Size of the RAM area to be managed by the OS. If set to zero
* then the whole available RAM is used. The core memory is made
* available to the heap allocator and/or can be used directly through
* the simplified core memory allocator.
*
* @note In order to let the OS manage the whole RAM the linker script must
* provide the @p __heap_base__ and @p __heap_end__ symbols.
* @note Requires @p CH_USE_MEMCORE.
*/
#if !defined(CH_MEMCORE_SIZE) || defined(__DOXYGEN__)
#define CH_MEMCORE_SIZE 0
#endif
/**
* @brief Idle thread automatic spawn suppression.
* @details When this option is activated the function @p chSysInit()
* does not spawn the idle thread automatically. The application has
* then the responsibility to do one of the following:
* - Spawn a custom idle thread at priority @p IDLEPRIO.
* - Change the main() thread priority to @p IDLEPRIO then enter
* an endless loop. In this scenario the @p main() thread acts as
* the idle thread.
* .
* @note Unless an idle thread is spawned the @p main() thread must not
* enter a sleep state.
*/
#if !defined(CH_NO_IDLE_THREAD) || defined(__DOXYGEN__)
#define CH_NO_IDLE_THREAD FALSE
#endif
/** @} */
/*===========================================================================*/
/**
* @name Performance options
* @{
*/
/*===========================================================================*/
/**
* @brief OS optimization.
* @details If enabled then time efficient rather than space efficient code
* is used when two possible implementations exist.
*
* @note This is not related to the compiler optimization options.
* @note The default is @p TRUE.
*/
#if !defined(CH_OPTIMIZE_SPEED) || defined(__DOXYGEN__)
#define CH_OPTIMIZE_SPEED TRUE
#endif
/** @} */
/*===========================================================================*/
/**
* @name Subsystem options
* @{
*/
/*===========================================================================*/
/**
* @brief Threads registry APIs.
* @details If enabled then the registry APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_REGISTRY) || defined(__DOXYGEN__)
#define CH_USE_REGISTRY FALSE
#endif
/**
* @brief Threads synchronization APIs.
* @details If enabled then the @p chThdWait() function is included in
* the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_WAITEXIT) || defined(__DOXYGEN__)
#define CH_USE_WAITEXIT TRUE
#endif
/**
* @brief Semaphores APIs.
* @details If enabled then the Semaphores APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_SEMAPHORES) || defined(__DOXYGEN__)
#define CH_USE_SEMAPHORES TRUE
#endif
/**
* @brief Semaphores queuing mode.
* @details If enabled then the threads are enqueued on semaphores by
* priority rather than in FIFO order.
*
* @note The default is @p FALSE. Enable this if you have special requirements.
* @note Requires @p CH_USE_SEMAPHORES.
*/
#if !defined(CH_USE_SEMAPHORES_PRIORITY) || defined(__DOXYGEN__)
#define CH_USE_SEMAPHORES_PRIORITY FALSE
#endif
/**
* @brief Atomic semaphore API.
* @details If enabled then the semaphores the @p chSemSignalWait() API
* is included in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_SEMAPHORES.
*/
#if !defined(CH_USE_SEMSW) || defined(__DOXYGEN__)
#define CH_USE_SEMSW TRUE
#endif
/**
* @brief Mutexes APIs.
* @details If enabled then the mutexes APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MUTEXES) || defined(__DOXYGEN__)
#define CH_USE_MUTEXES TRUE
#endif
/**
* @brief Conditional Variables APIs.
* @details If enabled then the conditional variables APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_MUTEXES.
*/
#if !defined(CH_USE_CONDVARS) || defined(__DOXYGEN__)
#define CH_USE_CONDVARS TRUE
#endif
/**
* @brief Conditional Variables APIs with timeout.
* @details If enabled then the conditional variables APIs with timeout
* specification are included in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_CONDVARS.
*/
#if !defined(CH_USE_CONDVARS_TIMEOUT) || defined(__DOXYGEN__)
#define CH_USE_CONDVARS_TIMEOUT TRUE
#endif
/**
* @brief Events Flags APIs.
* @details If enabled then the event flags APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_EVENTS) || defined(__DOXYGEN__)
#define CH_USE_EVENTS TRUE
#endif
/**
* @brief Events Flags APIs with timeout.
* @details If enabled then the events APIs with timeout specification
* are included in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_EVENTS.
*/
#if !defined(CH_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__)
#define CH_USE_EVENTS_TIMEOUT TRUE
#endif
/**
* @brief Synchronous Messages APIs.
* @details If enabled then the synchronous messages APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MESSAGES) || defined(__DOXYGEN__)
#define CH_USE_MESSAGES TRUE
#endif
/**
* @brief Synchronous Messages queuing mode.
* @details If enabled then messages are served by priority rather than in
* FIFO order.
*
* @note The default is @p FALSE. Enable this if you have special requirements.
* @note Requires @p CH_USE_MESSAGES.
*/
#if !defined(CH_USE_MESSAGES_PRIORITY) || defined(__DOXYGEN__)
#define CH_USE_MESSAGES_PRIORITY FALSE
#endif
/**
* @brief Mailboxes APIs.
* @details If enabled then the asynchronous messages (mailboxes) APIs are
* included in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_SEMAPHORES.
*/
#if !defined(CH_USE_MAILBOXES) || defined(__DOXYGEN__)
#define CH_USE_MAILBOXES TRUE
#endif
/**
* @brief I/O Queues APIs.
* @details If enabled then the I/O queues APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_QUEUES) || defined(__DOXYGEN__)
#define CH_USE_QUEUES TRUE
#endif
/**
* @brief Core Memory Manager APIs.
* @details If enabled then the core memory manager APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MEMCORE) || defined(__DOXYGEN__)
#define CH_USE_MEMCORE TRUE
#endif
/**
* @brief Heap Allocator APIs.
* @details If enabled then the memory heap allocator APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_MEMCORE and either @p CH_USE_MUTEXES or
* @p CH_USE_SEMAPHORES.
* @note Mutexes are recommended.
*/
#if !defined(CH_USE_HEAP) || defined(__DOXYGEN__)
#define CH_USE_HEAP TRUE
#endif
/**
* @brief C-runtime allocator.
* @details If enabled the the heap allocator APIs just wrap the C-runtime
* @p malloc() and @p free() functions.
*
* @note The default is @p FALSE.
* @note Requires @p CH_USE_HEAP.
* @note The C-runtime may or may not require @p CH_USE_MEMCORE, see the
* appropriate documentation.
*/
#if !defined(CH_USE_MALLOC_HEAP) || defined(__DOXYGEN__)
#define CH_USE_MALLOC_HEAP FALSE
#endif
/**
* @brief Memory Pools Allocator APIs.
* @details If enabled then the memory pools allocator APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MEMPOOLS) || defined(__DOXYGEN__)
#define CH_USE_MEMPOOLS TRUE
#endif
/**
* @brief Dynamic Threads APIs.
* @details If enabled then the dynamic threads creation APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_WAITEXIT.
* @note Requires @p CH_USE_HEAP and/or @p CH_USE_MEMPOOLS.
*/
#if !defined(CH_USE_DYNAMIC) || defined(__DOXYGEN__)
#define CH_USE_DYNAMIC TRUE
#endif
/** @} */
/*===========================================================================*/
/**
* @name Debug options
* @{
*/
/*===========================================================================*/
/**
* @brief Debug option, system state check.
* @details If enabled the correct call protocol for system APIs is checked
* at runtime.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_SYSTEM_STATE_CHECK) || defined(__DOXYGEN__)
#define CH_DBG_SYSTEM_STATE_CHECK TRUE
#endif
/**
* @brief Debug option, parameters checks.
* @details If enabled then the checks on the API functions input
* parameters are activated.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_CHECKS) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_CHECKS TRUE
#endif
/**
* @brief Debug option, consistency checks.
* @details If enabled then all the assertions in the kernel code are
* activated. This includes consistency checks inside the kernel,
* runtime anomalies and port-defined checks.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_ASSERTS) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_ASSERTS TRUE
#endif
/**
* @brief Debug option, trace buffer.
* @details If enabled then the context switch circular trace buffer is
* activated.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_TRACE) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_TRACE FALSE
#endif
/**
* @brief Debug option, stack checks.
* @details If enabled then a runtime stack check is performed.
*
* @note The default is @p FALSE.
* @note The stack check is performed in a architecture/port dependent way.
* It may not be implemented or some ports.
* @note The default failure mode is to halt the system with the global
* @p panic_msg variable set to @p NULL.
*/
#if !defined(CH_DBG_ENABLE_STACK_CHECK) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_STACK_CHECK TRUE
#endif
/**
* @brief Debug option, stacks initialization.
* @details If enabled then the threads working area is filled with a byte
* value when a thread is created. This can be useful for the
* runtime measurement of the used stack.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_FILL_THREADS) || defined(__DOXYGEN__)
#define CH_DBG_FILL_THREADS TRUE
#endif
/**
* @brief Debug option, threads profiling.
* @details If enabled then a field is added to the @p Thread structure that
* counts the system ticks occurred while executing the thread.
*
* @note The default is @p TRUE.
* @note This debug option is defaulted to TRUE because it is required by
* some test cases into the test suite.
*/
#if !defined(CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__)
#define CH_DBG_THREADS_PROFILING TRUE
#endif
/** @} */
/*===========================================================================*/
/**
* @name Kernel hooks
* @{
*/
/*===========================================================================*/
/**
* @brief Threads descriptor structure extension.
* @details User fields added to the end of the @p Thread structure.
*/
#if !defined(THREAD_EXT_FIELDS) || defined(__DOXYGEN__)
#define THREAD_EXT_FIELDS \
/* Add threads custom fields here.*/ \
uint32_t switches; \
uint32_t start_ticks; \
uint32_t total_ticks;
#endif
/**
* @brief Threads initialization hook.
* @details User initialization code added to the @p chThdInit() API.
*
* @note It is invoked from within @p chThdInit() and implicitly from all
* the threads creation APIs.
*/
#if !defined(THREAD_EXT_INIT_HOOK) || defined(__DOXYGEN__)
#define THREAD_EXT_INIT_HOOK(tp) { \
/* Add threads initialization code here.*/ \
tp->switches = 0; \
tp->start_ticks = 0; \
tp->total_ticks = 0; \
}
#endif
/**
* @brief Threads finalization hook.
* @details User finalization code added to the @p chThdExit() API.
*
* @note It is inserted into lock zone.
* @note It is also invoked when the threads simply return in order to
* terminate.
*/
#if !defined(THREAD_EXT_EXIT_HOOK) || defined(__DOXYGEN__)
#define THREAD_EXT_EXIT_HOOK(tp) { \
/* Add threads finalization code here.*/ \
}
#endif
/**
* @brief Context switch hook.
* @details This hook is invoked just before switching between threads.
*/
#if !defined(THREAD_CONTEXT_SWITCH_HOOK) || defined(__DOXYGEN__)
#define THREAD_CONTEXT_SWITCH_HOOK(ntp, otp) { \
/* System halt code here.*/ \
otp->switches++; \
ntp->start_ticks = *((volatile uint32_t*)0x400C4008); \
otp->total_ticks += (ntp->start_ticks - otp->start_ticks); \
}
#endif
/**
* @brief Idle Loop hook.
* @details This hook is continuously invoked by the idle thread loop.
*/
#if !defined(IDLE_LOOP_HOOK) || defined(__DOXYGEN__)
#define IDLE_LOOP_HOOK() { \
/* Idle loop code here.*/ \
}
#endif
/**
* @brief System tick event hook.
* @details This hook is invoked in the system tick handler immediately
* after processing the virtual timers queue.
*/
#if !defined(SYSTEM_TICK_EVENT_HOOK) || defined(__DOXYGEN__)
#define SYSTEM_TICK_EVENT_HOOK() { \
/* System tick event code here.*/ \
}
#endif
/**
* @brief System halt hook.
* @details This hook is invoked in case to a system halting error before
* the system is halted.
*/
#if !defined(SYSTEM_HALT_HOOK) || defined(__DOXYGEN__)
#define SYSTEM_HALT_HOOK() { \
/* System halt code here.*/ \
}
#endif
/** @} */
/*===========================================================================*/
/* Port-specific settings (override port settings defaulted in chcore.h). */
/*===========================================================================*/
/* NOTE: When changing this option you also have to enable or disable the FPU
in the project options.*/
#define CORTEX_USE_FPU TRUE
#define CORTEX_ENABLE_WFI_IDLE TRUE
#endif /* _CHCONF_H_ */
/** @} */

View File

@ -1,22 +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.
*/
#include "clock_recovery.hpp"

View File

@ -1,185 +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.
*/
#ifndef __CLOCK_RECOVERY_H__
#define __CLOCK_RECOVERY_H__
#include <cstddef>
#include <array>
#include <functional>
#include "linear_resampler.hpp"
namespace clock_recovery {
class GardnerTimingErrorDetector {
public:
static constexpr size_t samples_per_symbol { 2 };
/*
Expects retimed samples at a rate of twice the expected symbol rate.
Calculates timing error, sends symbol and error to handler.
*/
template<typename SymbolHandler>
void operator()(
const float in,
SymbolHandler symbol_handler
) {
/* NOTE: Algorithm is sensitive to input magnitude. Timing error value
* will scale proportionally. Best practice is to use error sign only.
*/
t[2] = t[1];
t[1] = t[0];
t[0] = in;
if( symbol_phase == 0 ) {
const auto symbol = t[0];
const float lateness = (t[0] - t[2]) * t[1];
symbol_handler(symbol, lateness);
}
symbol_phase = (symbol_phase + 1) % samples_per_symbol;
}
private:
std::array<float, 3> t { { 0.0f, 0.0f, 0.0f } };
size_t symbol_phase { 0 };
};
class LinearErrorFilter {
public:
LinearErrorFilter(
const float filter_alpha = 0.95f,
const float error_weight = -1.0f
) : filter_alpha { filter_alpha },
error_weight { error_weight }
{
}
float operator()(
const float error
) {
error_filtered = filter_alpha * error_filtered + (1.0f - filter_alpha) * error;
return error_filtered * error_weight;
}
private:
const float filter_alpha;
const float error_weight;
float error_filtered { 0.0f };
};
class FixedErrorFilter {
public:
FixedErrorFilter(
) {
}
FixedErrorFilter(
const float weight
) : weight_ { weight }
{
}
float operator()(
const float lateness
) const {
return (lateness < 0.0f) ? weight() : -weight();
}
float weight() const {
return weight_;
}
private:
float weight_ { 1.0f / 16.0f };
};
template<typename ErrorFilter>
class ClockRecovery {
public:
using SymbolHandler = std::function<void(const float)>;
ClockRecovery(
const float sampling_rate,
const float symbol_rate,
ErrorFilter error_filter,
SymbolHandler symbol_handler
) : symbol_handler { std::move(symbol_handler) }
{
configure(sampling_rate, symbol_rate, error_filter);
}
ClockRecovery(
SymbolHandler symbol_handler
) : symbol_handler { std::move(symbol_handler) }
{
}
void configure(
const float sampling_rate,
const float symbol_rate,
ErrorFilter error_filter
) {
resampler.configure(sampling_rate, symbol_rate * timing_error_detector.samples_per_symbol);
error_filter = error_filter;
}
void operator()(
const float baseband_sample
) {
resampler(baseband_sample,
[this](const float interpolated_sample) {
this->resampler_callback(interpolated_sample);
}
);
}
private:
dsp::interpolation::LinearResampler resampler;
GardnerTimingErrorDetector timing_error_detector;
ErrorFilter error_filter;
const SymbolHandler symbol_handler;
void resampler_callback(const float interpolated_sample) {
timing_error_detector(interpolated_sample,
[this](const float symbol, const float lateness) {
this->symbol_callback(symbol, lateness);
}
);
}
void symbol_callback(const float symbol, const float lateness) {
// NOTE: This check is to avoid std::function nullptr check, which
// brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code.
// TODO: Make symbol_handler known at compile time.
if( symbol_handler) {
symbol_handler(symbol);
}
const float adjustment = error_filter(lateness);
resampler.advance(adjustment);
}
};
} /* namespace clock_recovery */
#endif/*__CLOCK_RECOVERY_H__*/

View File

@ -1 +0,0 @@
More or less experimental stuff, mainly TX.

View File

@ -1,790 +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.
*/
#include "dsp_decimate.hpp"
#include <hal.h>
namespace dsp {
namespace decimate {
static inline complex32_t mac_fs4_shift(
const vec2_s16* const z,
const vec2_s16* const t,
const size_t index,
const complex32_t accum
) {
/* Accumulate sample * tap results for samples already in z buffer.
* Multiply using swap/negation to achieve Fs/4 shift.
* For iterations where samples are shifting out of z buffer (being discarded).
* Expect negated tap t[2] to accomodate instruction set limitations.
*/
const bool negated_t2 = index & 1;
const auto q1_i0 = z[index*2 + 0];
const auto i1_q0 = z[index*2 + 1];
const auto t1_t0 = t[index];
const auto real = negated_t2 ? smlsd(q1_i0, t1_t0, accum.real()) : smlad(q1_i0, t1_t0, accum.real());
const auto imag = negated_t2 ? smlad(i1_q0, t1_t0, accum.imag()) : smlsd(i1_q0, t1_t0, accum.imag());
return { real, imag };
}
static inline complex32_t mac_shift(
const vec2_s16* const z,
const vec2_s16* const t,
const size_t index,
const complex32_t accum
) {
/* Accumulate sample * tap results for samples already in z buffer.
* For iterations where samples are shifting out of z buffer (being discarded).
* real += i1 * t1 + i0 * t0
* imag += q1 * t1 + q0 * t0
*/
const auto i1_i0 = z[index*2 + 0];
const auto q1_q0 = z[index*2 + 1];
const auto t1_t0 = t[index];
const auto real = smlad(i1_i0, t1_t0, accum.real());
const auto imag = smlad(q1_q0, t1_t0, accum.imag());
return { real, imag };
}
static inline complex32_t mac_fs4_shift_and_store(
vec2_s16* const z,
const vec2_s16* const t,
const size_t decimation_factor,
const size_t index,
const complex32_t accum
) {
/* Accumulate sample * tap results for samples already in z buffer.
* Place new samples into z buffer.
* Expect negated tap t[2] to accomodate instruction set limitations.
*/
const bool negated_t2 = index & 1;
const auto q1_i0 = z[decimation_factor + index*2 + 0];
const auto i1_q0 = z[decimation_factor + index*2 + 1];
const auto t1_t0 = t[decimation_factor / 2 + index];
z[index*2 + 0] = q1_i0;
const auto real = negated_t2 ? smlsd(q1_i0, t1_t0, accum.real()) : smlad(q1_i0, t1_t0, accum.real());
z[index*2 + 1] = i1_q0;
const auto imag = negated_t2 ? smlad(i1_q0, t1_t0, accum.imag()) : smlsd(i1_q0, t1_t0, accum.imag());
return { real, imag };
}
static inline complex32_t mac_shift_and_store(
vec2_s16* const z,
const vec2_s16* const t,
const size_t decimation_factor,
const size_t index,
const complex32_t accum
) {
/* Accumulate sample * tap results for samples already in z buffer.
* Place new samples into z buffer.
* Expect negated tap t[2] to accomodate instruction set limitations.
*/
const auto i1_i0 = z[decimation_factor + index*2 + 0];
const auto q1_q0 = z[decimation_factor + index*2 + 1];
const auto t1_t0 = t[decimation_factor / 2 + index];
z[index*2 + 0] = i1_i0;
const auto real = smlad(i1_i0, t1_t0, accum.real());
z[index*2 + 1] = q1_q0;
const auto imag = smlad(q1_q0, t1_t0, accum.imag());
return { real, imag };
}
static inline complex32_t mac_fs4_shift_and_store_new_c8_samples(
vec2_s16* const z,
const vec2_s16* const t,
const vec4_s8* const in,
const size_t decimation_factor,
const size_t index,
const size_t length,
const complex32_t accum
) {
/* Accumulate sample * tap results for new samples.
* Place new samples into z buffer.
* Expect negated tap t[2] to accomodate instruction set limitations.
*/
const bool negated_t2 = index & 1;
const auto q1_i1_q0_i0 = in[index];
const auto t1_t0 = t[(length - decimation_factor) / 2 + index];
const auto i1_q1_i0_q0 = rev16(q1_i1_q0_i0);
const auto i1_q1_q0_i0 = pkhbt(q1_i1_q0_i0, i1_q1_i0_q0);
const auto q1_i0 = sxtb16(i1_q1_q0_i0);
const auto i1_q0 = sxtb16(i1_q1_q0_i0, 8);
z[length - decimation_factor * 2 + index*2 + 0] = q1_i0;
const auto real = negated_t2 ? smlsd(q1_i0, t1_t0, accum.real()) : smlad(q1_i0, t1_t0, accum.real());
z[length - decimation_factor * 2 + index*2 + 1] = i1_q0;
const auto imag = negated_t2 ? smlad(i1_q0, t1_t0, accum.imag()) : smlsd(i1_q0, t1_t0, accum.imag());
return { real, imag };
}
static inline complex32_t mac_shift_and_store_new_c16_samples(
vec2_s16* const z,
const vec2_s16* const t,
const vec2_s16* const in,
const size_t decimation_factor,
const size_t index,
const size_t length,
const complex32_t accum
) {
/* Accumulate sample * tap results for new samples.
* Place new samples into z buffer.
* Expect negated tap t[2] to accomodate instruction set limitations.
*/
const auto q0_i0 = in[index*2+0];
const auto q1_i1 = in[index*2+1];
const auto i1_i0 = pkhbt(q0_i0, q1_i1, 16);
const auto q1_q0 = pkhtb(q1_i1, q0_i0, 16);
const auto t1_t0 = t[(length - decimation_factor) / 2 + index];
z[length - decimation_factor * 2 + index*2 + 0] = i1_i0;
const auto real = smlad(i1_i0, t1_t0, accum.real());
z[length - decimation_factor * 2 + index*2 + 1] = q1_q0;
const auto imag = smlad(q1_q0, t1_t0, accum.imag());
return { real, imag };
}
static inline uint32_t scale_round_and_pack(
const complex32_t value,
const int32_t scale_factor
) {
/* Multiply 32-bit components of the complex<int32_t> by a scale factor,
* into int64_ts, then round to nearest LSB (1 << 32), saturate to 16 bits,
* and pack into a complex<int16_t>.
*/
const auto scaled_real = __SMMULR(value.real(), scale_factor);
const auto saturated_real = __SSAT(scaled_real, 16);
const auto scaled_imag = __SMMULR(value.imag(), scale_factor);
const auto saturated_imag = __SSAT(scaled_imag, 16);
return __PKHBT(saturated_real, saturated_imag, 16);
}
template<typename Tap>
static void taps_copy(
const Tap* const source,
Tap* const target,
const size_t count,
const bool shift_up
) {
const uint32_t negate_pattern = shift_up ? 0b1110 : 0b0100;
for(size_t i=0; i<count; i++) {
const bool negate = (negate_pattern >> (i & 3)) & 1;
target[i] = negate ? -source[i] : source[i];
}
}
// FIRC8xR16x24FS4Decim4 //////////////////////////////////////////////////
void FIRC8xR16x24FS4Decim4::configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale,
const Shift shift
) {
taps_copy(taps.data(), taps_.data(), taps_.size(), shift == Shift::Up);
output_scale = scale;
z_.fill({});
}
buffer_c16_t FIRC8xR16x24FS4Decim4::execute(
const buffer_c8_t& src,
const buffer_c16_t& dst
) {
vec2_s16* const z = static_cast<vec2_s16*>(__builtin_assume_aligned(z_.data(), 4));
const vec2_s16* const t = static_cast<vec2_s16*>(__builtin_assume_aligned(taps_.data(), 4));
uint32_t* const d = static_cast<uint32_t*>(__builtin_assume_aligned(dst.p, 4));
const auto k = output_scale;
const size_t count = src.count / decimation_factor;
for(size_t i=0; i<count; i++) {
const vec4_s8* const in = static_cast<const vec4_s8*>(__builtin_assume_aligned(&src.p[i * decimation_factor], 4));
complex32_t accum;
// Oldest samples are discarded.
accum = mac_fs4_shift(z, t, 0, accum);
accum = mac_fs4_shift(z, t, 1, accum);
// Middle samples are shifted earlier in the "z" delay buffer.
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 0, accum);
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 1, accum);
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 2, accum);
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 3, accum);
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 4, accum);
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 5, accum);
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 6, accum);
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 7, accum);
// Newest samples come from "in" buffer, are copied to "z" delay buffer.
accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 0, taps_count, accum);
accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 1, taps_count, accum);
d[i] = scale_round_and_pack(accum, k);
}
return {
dst.p,
count,
src.sampling_rate / decimation_factor
};
}
// FIRC8xR16x24FS4Decim8 //////////////////////////////////////////////////
void FIRC8xR16x24FS4Decim8::configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale,
const Shift shift
) {
taps_copy(taps.data(), taps_.data(), taps_.size(), shift == Shift::Up);
output_scale = scale;
z_.fill({});
}
buffer_c16_t FIRC8xR16x24FS4Decim8::execute(
const buffer_c8_t& src,
const buffer_c16_t& dst
) {
vec2_s16* const z = static_cast<vec2_s16*>(__builtin_assume_aligned(z_.data(), 4));
const vec2_s16* const t = static_cast<vec2_s16*>(__builtin_assume_aligned(taps_.data(), 4));
uint32_t* const d = static_cast<uint32_t*>(__builtin_assume_aligned(dst.p, 4));
const auto k = output_scale;
const size_t count = src.count / decimation_factor;
for(size_t i=0; i<count; i++) {
const vec4_s8* const in = static_cast<const vec4_s8*>(__builtin_assume_aligned(&src.p[i * decimation_factor], 4));
complex32_t accum;
// Oldest samples are discarded.
accum = mac_fs4_shift(z, t, 0, accum);
accum = mac_fs4_shift(z, t, 1, accum);
accum = mac_fs4_shift(z, t, 2, accum);
accum = mac_fs4_shift(z, t, 3, accum);
// Middle samples are shifted earlier in the "z" delay buffer.
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 0, accum);
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 1, accum);
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 2, accum);
accum = mac_fs4_shift_and_store(z, t, decimation_factor, 3, accum);
// Newest samples come from "in" buffer, are copied to "z" delay buffer.
accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 0, taps_count, accum);
accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 1, taps_count, accum);
accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 2, taps_count, accum);
accum = mac_fs4_shift_and_store_new_c8_samples(z, t, in, decimation_factor, 3, taps_count, accum);
d[i] = scale_round_and_pack(accum, k);
}
return {
dst.p,
count,
src.sampling_rate / decimation_factor
};
}
// FIRC16xR16x16Decim2 ////////////////////////////////////////////////////
void FIRC16xR16x16Decim2::configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale
) {
std::copy(taps.cbegin(), taps.cend(), taps_.begin());
output_scale = scale;
z_.fill({});
}
buffer_c16_t FIRC16xR16x16Decim2::execute(
const buffer_c16_t& src,
const buffer_c16_t& dst
) {
vec2_s16* const z = static_cast<vec2_s16*>(__builtin_assume_aligned(z_.data(), 4));
const vec2_s16* const t = static_cast<vec2_s16*>(__builtin_assume_aligned(taps_.data(), 4));
uint32_t* const d = static_cast<uint32_t*>(__builtin_assume_aligned(dst.p, 4));
const auto k = output_scale;
const size_t count = src.count / decimation_factor;
for(size_t i=0; i<count; i++) {
const vec2_s16* const in = static_cast<const vec2_s16*>(__builtin_assume_aligned(&src.p[i * decimation_factor], 4));
complex32_t accum;
// Oldest samples are discarded.
accum = mac_shift(z, t, 0, accum);
// Middle samples are shifted earlier in the "z" delay buffer.
accum = mac_shift_and_store(z, t, decimation_factor, 0, accum);
accum = mac_shift_and_store(z, t, decimation_factor, 1, accum);
accum = mac_shift_and_store(z, t, decimation_factor, 2, accum);
accum = mac_shift_and_store(z, t, decimation_factor, 3, accum);
accum = mac_shift_and_store(z, t, decimation_factor, 4, accum);
accum = mac_shift_and_store(z, t, decimation_factor, 5, accum);
// Newest samples come from "in" buffer, are copied to "z" delay buffer.
accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 0, taps_count, accum);
d[i] = scale_round_and_pack(accum, k);
}
return {
dst.p,
count,
src.sampling_rate / decimation_factor
};
}
// FIRC16xR16x32Decim8 ////////////////////////////////////////////////////
void FIRC16xR16x32Decim8::configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale
) {
std::copy(taps.cbegin(), taps.cend(), taps_.begin());
output_scale = scale;
z_.fill({});
}
buffer_c16_t FIRC16xR16x32Decim8::execute(
const buffer_c16_t& src,
const buffer_c16_t& dst
) {
vec2_s16* const z = static_cast<vec2_s16*>(__builtin_assume_aligned(z_.data(), 4));
const vec2_s16* const t = static_cast<vec2_s16*>(__builtin_assume_aligned(taps_.data(), 4));
uint32_t* const d = static_cast<uint32_t*>(__builtin_assume_aligned(dst.p, 4));
const auto k = output_scale;
const size_t count = src.count / decimation_factor;
for(size_t i=0; i<count; i++) {
const vec2_s16* const in = static_cast<const vec2_s16*>(__builtin_assume_aligned(&src.p[i * decimation_factor], 4));
complex32_t accum;
// Oldest samples are discarded.
accum = mac_shift(z, t, 0, accum);
accum = mac_shift(z, t, 1, accum);
accum = mac_shift(z, t, 2, accum);
accum = mac_shift(z, t, 3, accum);
// Middle samples are shifted earlier in the "z" delay buffer.
accum = mac_shift_and_store(z, t, decimation_factor, 0, accum);
accum = mac_shift_and_store(z, t, decimation_factor, 1, accum);
accum = mac_shift_and_store(z, t, decimation_factor, 2, accum);
accum = mac_shift_and_store(z, t, decimation_factor, 3, accum);
accum = mac_shift_and_store(z, t, decimation_factor, 4, accum);
accum = mac_shift_and_store(z, t, decimation_factor, 5, accum);
accum = mac_shift_and_store(z, t, decimation_factor, 6, accum);
accum = mac_shift_and_store(z, t, decimation_factor, 7, accum);
// Newest samples come from "in" buffer, are copied to "z" delay buffer.
accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 0, taps_count, accum);
accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 1, taps_count, accum);
accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 2, taps_count, accum);
accum = mac_shift_and_store_new_c16_samples(z, t, in, decimation_factor, 3, taps_count, accum);
d[i] = scale_round_and_pack(accum, k);
}
return {
dst.p,
count,
src.sampling_rate / decimation_factor
};
}
buffer_c16_t Complex8DecimateBy2CIC3::execute(const buffer_c8_t& src, const buffer_c16_t& dst) {
/* Decimates by two using a non-recursive third-order CIC filter.
*/
/* CIC filter (decimating by two):
* D_I0 = i3 * 1 + i2 * 3 + i1 * 3 + i0 * 1
* D_Q0 = q3 * 1 + q2 * 3 + q1 * 3 + q0 * 1
*
* D_I1 = i5 * 1 + i4 * 3 + i3 * 3 + i2 * 1
* D_Q1 = q5 * 1 + q4 * 3 + q3 * 3 + q2 * 1
*/
uint32_t i1_i0 = _i1_i0;
uint32_t q1_q0 = _q1_q0;
/* 3:1 Scaled by 32 to normalize output to +/-32768-ish. */
constexpr uint32_t scale_factor = 32;
constexpr uint32_t k_3_1 = 0x00030001 * scale_factor;
uint32_t* src_p = reinterpret_cast<uint32_t*>(&src.p[0]);
uint32_t* const src_end = reinterpret_cast<uint32_t*>(&src.p[src.count]);
uint32_t* dst_p = reinterpret_cast<uint32_t*>(&dst.p[0]);
while(src_p < src_end) {
const uint32_t q3_i3_q2_i2 = *(src_p++); // 3
const uint32_t q5_i5_q4_i4 = *(src_p++);
const uint32_t d_i0_partial = __SMUAD(k_3_1, i1_i0); // 1: = 3 * i1 + 1 * i0
const uint32_t i3_i2 = __SXTB16(q3_i3_q2_i2, 0); // 1: (q3_i3_q2_i2 ror 0)[23:16]:(q3_i3_q2_i2 ror 0)[7:0]
const uint32_t d_i0 = __SMLADX(k_3_1, i3_i2, d_i0_partial); // 1: + 3 * i2 + 1 * i3
const uint32_t d_q0_partial = __SMUAD(k_3_1, q1_q0); // 1: = 3 * q1 * 1 * q0
const uint32_t q3_q2 = __SXTB16(q3_i3_q2_i2, 8); // 1: (q3_i3_q2_i2 ror 8)[23:16]:(q3_i3_q2_i2 ror 8)[7:0]
const uint32_t d_q0 = __SMLADX(k_3_1, q3_q2, d_q0_partial); // 1: + 3 * q2 + 1 * q3
const uint32_t d_q0_i0 = __PKHBT(d_i0, d_q0, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
const uint32_t d_i1_partial = __SMUAD(k_3_1, i3_i2); // 1: = 3 * i3 + 1 * i2
const uint32_t i5_i4 = __SXTB16(q5_i5_q4_i4, 0); // 1: (q5_i5_q4_i4 ror 0)[23:16]:(q5_i5_q4_i4 ror 0)[7:0]
const uint32_t d_i1 = __SMLADX(k_3_1, i5_i4, d_i1_partial); // 1: + 1 * i5 + 3 * i4
const uint32_t d_q1_partial = __SMUAD(k_3_1, q3_q2); // 1: = 3 * q3 * 1 * q2
const uint32_t q5_q4 = __SXTB16(q5_i5_q4_i4, 8); // 1: (q5_i5_q4_i4 ror 8)[23:16]:(q5_i5_q4_i4 ror 8)[7:0]
const uint32_t d_q1 = __SMLADX(k_3_1, q5_q4, d_q1_partial); // 1: + 1 * q5 + 3 * q4
const uint32_t d_q1_i1 = __PKHBT(d_i1, d_q1, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
*(dst_p++) = d_q0_i0; // 3
*(dst_p++) = d_q1_i1;
i1_i0 = i5_i4;
q1_q0 = q5_q4;
}
_i1_i0 = i1_i0;
_q1_q0 = q1_q0;
return { dst.p, src.count / 2, src.sampling_rate / 2 };
}
buffer_c16_t TranslateByFSOver4AndDecimateBy2CIC3::execute(const buffer_c8_t& src, const buffer_c16_t& dst) {
/* Translates incoming complex<int8_t> samples by -fs/4,
* decimates by two using a non-recursive third-order CIC filter.
*/
/* Derivation of algorithm:
* Original CIC filter (decimating by two):
* D_I0 = i3 * 1 + i2 * 3 + i1 * 3 + i0 * 1
* D_Q0 = q3 * 1 + q2 * 3 + q1 * 3 + q0 * 1
*
* D_I1 = i5 * 1 + i4 * 3 + i3 * 3 + i2 * 1
* D_Q1 = q5 * 1 + q4 * 3 + q3 * 3 + q2 * 1
*
* Translate -fs/4, phased 180 degrees, accomplished by complex multiplication
* of complex length-4 sequence:
*
* Substitute:
* i0 = -i0, q0 = -q0
* i1 = -q1, q1 = i1
* i2 = i2, q2 = q2
* i3 = q3, q3 = -i3
* i4 = -i4, q4 = -q4
* i5 = -q5, q5 = i5
*
* Resulting taps (with decimation by 2, four samples in, two samples out):
* D_I0 = q3 * 1 + i2 * 3 + -q1 * 3 + -i0 * 1
* D_Q0 = -i3 * 1 + q2 * 3 + i1 * 3 + -q0 * 1
*
* D_I1 = -q5 * 1 + -i4 * 3 + q3 * 3 + i2 * 1
* D_Q1 = i5 * 1 + -q4 * 3 + -i3 * 3 + q2 * 1
*/
// 6 cycles per complex input sample, not including loop overhead.
uint32_t q1_i0 = _q1_i0;
uint32_t q0_i1 = _q0_i1;
/* 3:1 Scaled by 32 to normalize output to +/-32768-ish. */
constexpr uint32_t scale_factor = 32;
const uint32_t k_3_1 = 0x00030001 * scale_factor;
uint32_t* src_p = reinterpret_cast<uint32_t*>(&src.p[0]);
uint32_t* const src_end = reinterpret_cast<uint32_t*>(&src.p[src.count]);
uint32_t* dst_p = reinterpret_cast<uint32_t*>(&dst.p[0]);
while(src_p < src_end) {
const uint32_t q3_i3_q2_i2 = *(src_p++); // 3
const uint32_t q5_i5_q4_i4 = *(src_p++);
const uint32_t i2_i3 = __SXTB16(q3_i3_q2_i2, 16); // 1: (q3_i3_q2_i2 ror 16)[23:16]:(q3_i3_q2_i2 ror 16)[7:0]
const uint32_t q3_q2 = __SXTB16(q3_i3_q2_i2, 8); // 1: (q3_i3_q2_i2 ror 8)[23:16]:(q3_i3_q2_i2 ror 8)[7:0]
const uint32_t i2_q3 = __PKHTB(i2_i3, q3_q2, 16); // 1: Rn[31:16]:(Rm>>16)[15:0]
const uint32_t i3_q2 = __PKHBT(q3_q2, i2_i3, 16); // 1:(Rm<<16)[31:16]:Rn[15:0]
// D_I0 = 3 * (i2 - q1) + (q3 - i0)
const uint32_t i2_m_q1_q3_m_i0 = __QSUB16(i2_q3, q1_i0); // 1: Rn[31:16]-Rm[31:16]:Rn[15:0]-Rm[15:0]
const uint32_t d_i0 = __SMUAD(k_3_1, i2_m_q1_q3_m_i0); // 1: Rm[15:0]*Rs[15:0]+Rm[31:16]*Rs[31:16]
// D_Q0 = 3 * (q2 + i1) - (i3 + q0)
const uint32_t i3_p_q0_q2_p_i1 = __QADD16(i3_q2, q0_i1); // 1: Rn[31:16]+Rm[31:16]:Rn[15:0]+Rm[15:0]
const uint32_t d_q0 = __SMUSDX(i3_p_q0_q2_p_i1, k_3_1); // 1: Rm[15:0]*Rs[31:16]Rm[31:16]*RsX[15:0]
const uint32_t d_q0_i0 = __PKHBT(d_i0, d_q0, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
const uint32_t i5_i4 = __SXTB16(q5_i5_q4_i4, 0); // 1: (q5_i5_q4_i4 ror 0)[23:16]:(q5_i5_q4_i4 ror 0)[7:0]
const uint32_t q4_q5 = __SXTB16(q5_i5_q4_i4, 24); // 1: (q5_i5_q4_i4 ror 24)[23:16]:(q5_i5_q4_i4 ror 24)[7:0]
const uint32_t q4_i5 = __PKHTB(q4_q5, i5_i4, 16); // 1: Rn[31:16]:(Rm>>16)[15:0]
const uint32_t q5_i4 = __PKHBT(i5_i4, q4_q5, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
// D_I1 = (i2 - q5) + 3 * (q3 - i4)
const uint32_t i2_m_q5_q3_m_i4 = __QSUB16(i2_q3, q5_i4); // 1: Rn[31:16]-Rm[31:16]:Rn[15:0]-Rm[15:0]
const uint32_t d_i1 = __SMUADX(i2_m_q5_q3_m_i4, k_3_1); // 1: Rm[15:0]*Rs[31:16]+Rm[31:16]*Rs[15:0]
// D_Q1 = (i5 + q2) - 3 * (q4 + i3)
const uint32_t q4_p_i3_i5_p_q2 = __QADD16(q4_i5, i3_q2); // 1: Rn[31:16]+Rm[31:16]:Rn[15:0]+Rm[15:0]
const uint32_t d_q1 = __SMUSD(k_3_1, q4_p_i3_i5_p_q2); // 1: Rm[15:0]*Rs[15:0]Rm[31:16]*Rs[31:16]
const uint32_t d_q1_i1 = __PKHBT(d_i1, d_q1, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
*(dst_p++) = d_q0_i0; // 3
*(dst_p++) = d_q1_i1;
q1_i0 = q5_i4;
q0_i1 = q4_i5;
}
_q1_i0 = q1_i0;
_q0_i1 = q0_i1;
return { dst.p, src.count / 2, src.sampling_rate / 2 };
}
buffer_c16_t DecimateBy2CIC3::execute(
const buffer_c16_t& src,
const buffer_c16_t& dst
) {
/* Complex non-recursive 3rd-order CIC filter (taps 1,3,3,1).
* Gain of 8.
* Consumes 16 bytes (4 s16:s16 samples) per loop iteration,
* Produces 8 bytes (2 s16:s16 samples) per loop iteration.
*/
uint32_t t1 = _iq0;
uint32_t t2 = _iq1;
const uint32_t taps = 0x00000003;
auto s = src.p;
auto d = dst.p;
const auto d_end = &dst.p[src.count / 2];
while(d < d_end) {
uint32_t i = __SXTH(t1, 0); /* 1: I0 */
uint32_t q = __SXTH(t1, 16); /* 1: Q0 */
i = __SMLABB(t2, taps, i); /* 1: I1*3 + I0 */
q = __SMLATB(t2, taps, q); /* 1: Q1*3 + Q0 */
const uint32_t t3 = *__SIMD32(s)++; /* 3: Q2:I2 */
const uint32_t t4 = *__SIMD32(s)++; /* Q3:I3 */
i = __SMLABB(t3, taps, i); /* 1: I2*3 + I1*3 + I0 */
q = __SMLATB(t3, taps, q); /* 1: Q2*3 + Q1*3 + Q0 */
int32_t si0 = __SXTAH(i, t4, 0); /* 1: I3 + Q2*3 + Q1*3 + Q0 */
int32_t sq0 = __SXTAH(q, t4, 16); /* 1: Q3 + Q2*3 + Q1*3 + Q0 */
i = __BFI(si0 / 8, sq0 / 8, 16, 16); /* 1: D2_Q0:D2_I0 */
*__SIMD32(d)++ = i; /* D2_Q0:D2_I0 */
i = __SXTH(t3, 0); /* 1: I2 */
q = __SXTH(t3, 16); /* 1: Q2 */
i = __SMLABB(t4, taps, i); /* 1: I3*3 + I2 */
q = __SMLATB(t4, taps, q); /* 1: Q3*3 + Q2 */
t1 = *__SIMD32(s)++; /* 3: Q4:I4 */
t2 = *__SIMD32(s)++; /* Q5:I5 */
i = __SMLABB(t1, taps, i); /* 1: I4*3 + I3*3 + I2 */
q = __SMLATB(t1, taps, q); /* 1: Q4*3 + Q3*3 + Q2 */
int32_t si1 = __SXTAH(i, t2, 0) ; /* 1: I5 + Q4*3 + Q3*3 + Q2 */
int32_t sq1 = __SXTAH(q, t2, 16); /* 1: Q5 + Q4*3 + Q3*3 + Q2 */
i = __BFI(si1 / 8, sq1 / 8, 16, 16); /* 1: D2_Q1:D2_I1 */
*__SIMD32(d)++ = i; /* D2_Q1:D2_I1 */
}
_iq0 = t1;
_iq1 = t2;
return { dst.p, src.count / 2, src.sampling_rate / 2 };
}
void FIR64AndDecimateBy2Real::configure(
const std::array<int16_t, taps_count>& new_taps
) {
std::copy(new_taps.cbegin(), new_taps.cend(), taps.begin());
}
buffer_s16_t FIR64AndDecimateBy2Real::execute(
const buffer_s16_t& src,
const buffer_s16_t& dst
) {
/* int16_t input (sample count "n" must be multiple of 4)
* -> int16_t output, decimated by 2.
* taps are normalized to 1 << 16 == 1.0.
*/
auto src_p = src.p;
auto dst_p = dst.p;
int32_t n = src.count;
for(; n>0; n-=2) {
z[taps_count-2] = *(src_p++);
z[taps_count-1] = *(src_p++);
int32_t t = 0;
for(size_t j=0; j<taps_count; j+=4) {
t += z[j+0] * taps[j+0];
t += z[j+1] * taps[j+1];
t += z[j+2] * taps[j+2];
t += z[j+3] * taps[j+3];
z[j+0] = z[j+0+2];
z[j+1] = z[j+1+2];
z[j+2] = z[j+2+2];
z[j+3] = z[j+3+2];
}
*(dst_p++) = t / 65536;
}
return { dst.p, src.count / 2, src.sampling_rate / 2 };
}
void FIRAndDecimateComplex::configure_common(
const size_t taps_count, const size_t decimation_factor
) {
samples_ = std::make_unique<samples_t>(taps_count);
taps_reversed_ = std::make_unique<taps_t>(taps_count);
taps_count_ = taps_count;
decimation_factor_ = decimation_factor;
}
buffer_c16_t FIRAndDecimateComplex::execute(
const buffer_c16_t& src,
const buffer_c16_t& dst
) {
/* int16_t input (sample count "n" must be multiple of decimation_factor)
* -> int16_t output, decimated by decimation_factor.
* taps are normalized to 1 << 16 == 1.0.
*/
const auto output_sampling_rate = src.sampling_rate / decimation_factor_;
const size_t output_samples = src.count / decimation_factor_;
sample_t* dst_p = dst.p;
const buffer_c16_t result { dst.p, output_samples, output_sampling_rate };
const sample_t* src_p = src.p;
size_t outer_count = output_samples;
while(outer_count > 0) {
/* Put new samples into delay buffer */
auto z_new_p = &samples_[taps_count_ - decimation_factor_];
for(size_t i=0; i<decimation_factor_; i++) {
*__SIMD32(z_new_p)++ = *__SIMD32(src_p)++;
}
size_t loop_count = taps_count_ / 8;
auto t_p = &taps_reversed_[0];
auto z_p = &samples_[0];
int64_t t_real = 0;
int64_t t_imag = 0;
while(loop_count > 0) {
const auto tap0 = *__SIMD32(t_p)++;
const auto sample0 = *__SIMD32(z_p)++;
const auto tap1 = *__SIMD32(t_p)++;
const auto sample1 = *__SIMD32(z_p)++;
t_real = __SMLSLD(sample0, tap0, t_real);
t_imag = __SMLALDX(sample0, tap0, t_imag);
t_real = __SMLSLD(sample1, tap1, t_real);
t_imag = __SMLALDX(sample1, tap1, t_imag);
const auto tap2 = *__SIMD32(t_p)++;
const auto sample2 = *__SIMD32(z_p)++;
const auto tap3 = *__SIMD32(t_p)++;
const auto sample3 = *__SIMD32(z_p)++;
t_real = __SMLSLD(sample2, tap2, t_real);
t_imag = __SMLALDX(sample2, tap2, t_imag);
t_real = __SMLSLD(sample3, tap3, t_real);
t_imag = __SMLALDX(sample3, tap3, t_imag);
const auto tap4 = *__SIMD32(t_p)++;
const auto sample4 = *__SIMD32(z_p)++;
const auto tap5 = *__SIMD32(t_p)++;
const auto sample5 = *__SIMD32(z_p)++;
t_real = __SMLSLD(sample4, tap4, t_real);
t_imag = __SMLALDX(sample4, tap4, t_imag);
t_real = __SMLSLD(sample5, tap5, t_real);
t_imag = __SMLALDX(sample5, tap5, t_imag);
const auto tap6 = *__SIMD32(t_p)++;
const auto sample6 = *__SIMD32(z_p)++;
const auto tap7 = *__SIMD32(t_p)++;
const auto sample7 = *__SIMD32(z_p)++;
t_real = __SMLSLD(sample6, tap6, t_real);
t_imag = __SMLALDX(sample6, tap6, t_imag);
t_real = __SMLSLD(sample7, tap7, t_real);
t_imag = __SMLALDX(sample7, tap7, t_imag);
loop_count--;
}
/* TODO: Re-evaluate whether saturation is performed, normalization,
* all that jazz.
*/
const int32_t r = t_real >> 16;
const int32_t i = t_imag >> 16;
const int32_t r_sat = __SSAT(r, 16);
const int32_t i_sat = __SSAT(i, 16);
*__SIMD32(dst_p)++ = __PKHBT(
r_sat,
i_sat,
16
);
/* Shift sample buffer left/down by decimation factor. */
const size_t unroll_factor = 4;
size_t shift_count = (taps_count_ - decimation_factor_) / unroll_factor;
sample_t* t = &samples_[0];
const sample_t* s = &samples_[decimation_factor_];
while(shift_count > 0) {
*__SIMD32(t)++ = *__SIMD32(s)++;
*__SIMD32(t)++ = *__SIMD32(s)++;
*__SIMD32(t)++ = *__SIMD32(s)++;
*__SIMD32(t)++ = *__SIMD32(s)++;
shift_count--;
}
shift_count = (taps_count_ - decimation_factor_) % unroll_factor;
while(shift_count > 0) {
*(t++) = *(s++);
shift_count--;
}
outer_count--;
}
return result;
}
buffer_s16_t DecimateBy2CIC4Real::execute(
const buffer_s16_t& src,
const buffer_s16_t& dst
) {
auto src_p = src.p;
auto dst_p = dst.p;
int32_t n = src.count;
for(; n>0; n-=2) {
/* TODO: Probably a lot of room to optimize... */
z[0] = z[2];
z[1] = z[3];
z[2] = z[4];
z[3] = *(src_p++);
z[4] = *(src_p++);
int32_t t = z[0] + z[1] * 4 + z[2] * 6 + z[3] * 4 + z[4];
*(dst_p++) = t / 16;
}
return { dst.p, src.count / 2, src.sampling_rate / 2 };
}
} /* namespace decimate */
} /* namespace dsp */

View File

@ -1,268 +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.
*/
#ifndef __DSP_DECIMATE_H__
#define __DSP_DECIMATE_H__
#include <cstdint>
#include <array>
#include <memory>
#include <algorithm>
#include "utility.hpp"
#include "dsp_types.hpp"
#include "simd.hpp"
namespace dsp {
namespace decimate {
class Complex8DecimateBy2CIC3 {
public:
buffer_c16_t execute(
const buffer_c8_t& src,
const buffer_c16_t& dst
);
private:
uint32_t _i1_i0 { 0 };
uint32_t _q1_q0 { 0 };
};
class TranslateByFSOver4AndDecimateBy2CIC3 {
public:
buffer_c16_t execute(
const buffer_c8_t& src,
const buffer_c16_t& dst
);
private:
uint32_t _q1_i0 { 0 };
uint32_t _q0_i1 { 0 };
};
class DecimateBy2CIC3 {
public:
buffer_c16_t execute(
const buffer_c16_t& src,
const buffer_c16_t& dst
);
private:
uint32_t _iq0 { 0 };
uint32_t _iq1 { 0 };
};
class FIR64AndDecimateBy2Real {
public:
static constexpr size_t taps_count = 64;
void configure(
const std::array<int16_t, taps_count>& taps
);
buffer_s16_t execute(
const buffer_s16_t& src,
const buffer_s16_t& dst
);
private:
std::array<int16_t, taps_count + 2> z;
std::array<int16_t, taps_count> taps;
};
class FIRC8xR16x24FS4Decim4 {
public:
static constexpr size_t taps_count = 24;
static constexpr size_t decimation_factor = 4;
using sample_t = complex8_t;
using tap_t = int16_t;
enum class Shift : bool {
Down = true,
Up = false
};
void configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale,
const Shift shift = Shift::Down
);
buffer_c16_t execute(
const buffer_c8_t& src,
const buffer_c16_t& dst
);
private:
std::array<vec2_s16, taps_count - decimation_factor> z_;
std::array<tap_t, taps_count> taps_;
int32_t output_scale = 0;
};
class FIRC8xR16x24FS4Decim8 {
public:
static constexpr size_t taps_count = 24;
static constexpr size_t decimation_factor = 8;
using sample_t = complex8_t;
using tap_t = int16_t;
enum class Shift : bool {
Down = true,
Up = false
};
void configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale,
const Shift shift = Shift::Down
);
buffer_c16_t execute(
const buffer_c8_t& src,
const buffer_c16_t& dst
);
private:
std::array<vec2_s16, taps_count - decimation_factor> z_;
std::array<tap_t, taps_count> taps_;
int32_t output_scale = 0;
};
class FIRC16xR16x16Decim2 {
public:
static constexpr size_t taps_count = 16;
static constexpr size_t decimation_factor = 2;
using sample_t = complex16_t;
using tap_t = int16_t;
void configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale
);
buffer_c16_t execute(
const buffer_c16_t& src,
const buffer_c16_t& dst
);
private:
std::array<vec2_s16, taps_count - decimation_factor> z_;
std::array<tap_t, taps_count> taps_;
int32_t output_scale = 0;
};
class FIRC16xR16x32Decim8 {
public:
static constexpr size_t taps_count = 32;
static constexpr size_t decimation_factor = 8;
using sample_t = complex16_t;
using tap_t = int16_t;
void configure(
const std::array<tap_t, taps_count>& taps,
const int32_t scale
);
buffer_c16_t execute(
const buffer_c16_t& src,
const buffer_c16_t& dst
);
private:
std::array<vec2_s16, taps_count - decimation_factor> z_;
std::array<tap_t, taps_count> taps_;
int32_t output_scale = 0;
};
class FIRAndDecimateComplex {
public:
using sample_t = complex16_t;
using tap_t = complex16_t;
using taps_t = tap_t[];
/* NOTE! Current code makes an assumption that block of samples to be
* processed will be a multiple of the taps_count.
*/
FIRAndDecimateComplex(
) : taps_count_ { 0 },
decimation_factor_ { 1 }
{
}
template<typename T>
void configure(
const T& taps,
const size_t decimation_factor
) {
configure(taps.data(), taps.size(), decimation_factor);
}
buffer_c16_t execute(
const buffer_c16_t& src,
const buffer_c16_t& dst
);
private:
using samples_t = sample_t[];
std::unique_ptr<samples_t> samples_;
std::unique_ptr<taps_t> taps_reversed_;
size_t taps_count_;
size_t decimation_factor_;
template<typename T>
void configure(
const T* const taps,
const size_t taps_count,
const size_t decimation_factor
) {
configure_common(taps_count, decimation_factor);
std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]);
}
void configure_common(
const size_t taps_count,
const size_t decimation_factor
);
};
class DecimateBy2CIC4Real {
public:
buffer_s16_t execute(
const buffer_s16_t& src,
const buffer_s16_t& dst
);
private:
int16_t z[5];
};
} /* namespace decimate */
} /* namespace dsp */
#endif/*__DSP_DECIMATE_H__*/

View File

@ -1,151 +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.
*/
#include "dsp_demodulate.hpp"
#include "complex.hpp"
#include "fxpt_atan2.hpp"
#include "utility_m4.hpp"
#include <hal.h>
namespace dsp {
namespace demodulate {
buffer_f32_t AM::execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
) {
const auto src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while(src_p < src_end) {
const uint32_t sample0 = *__SIMD32(src_p)++;
const uint32_t sample1 = *__SIMD32(src_p)++;
const uint32_t mag_sq0 = __SMUAD(sample0, sample0);
const uint32_t mag_sq1 = __SMUAD(sample1, sample1);
*(dst_p++) = __builtin_sqrtf(mag_sq0) * k;
*(dst_p++) = __builtin_sqrtf(mag_sq1) * k;
}
return { dst.p, src.count, src.sampling_rate };
}
buffer_f32_t SSB::execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
) {
const complex16_t* src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while(src_p < src_end) {
*(dst_p++) = (src_p++)->real() * k;
*(dst_p++) = (src_p++)->real() * k;
*(dst_p++) = (src_p++)->real() * k;
*(dst_p++) = (src_p++)->real() * k;
}
return { dst.p, src.count, src.sampling_rate };
}
/*
static inline float angle_approx_4deg0(const complex32_t t) {
const auto x = static_cast<float>(t.imag()) / static_cast<float>(t.real());
return 16384.0f * x;
}
*/
static inline float angle_approx_0deg27(const complex32_t t) {
if( t.real() ) {
const auto x = static_cast<float>(t.imag()) / static_cast<float>(t.real());
return x / (1.0f + 0.28086f * x * x);
} else {
return (t.imag() < 0) ? -1.5707963268f : 1.5707963268f;
}
}
static inline float angle_precise(const complex32_t t) {
return atan2f(t.imag(), t.real());
}
buffer_f32_t FM::execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
) {
auto z = z_;
const auto src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while(src_p < src_end) {
const auto s0 = *__SIMD32(src_p)++;
const auto s1 = *__SIMD32(src_p)++;
const auto t0 = multiply_conjugate_s16_s32(s0, z);
const auto t1 = multiply_conjugate_s16_s32(s1, s0);
z = s1;
*(dst_p++) = angle_precise(t0) * kf;
*(dst_p++) = angle_precise(t1) * kf;
}
z_ = z;
return { dst.p, src.count, src.sampling_rate };
}
buffer_s16_t FM::execute(
const buffer_c16_t& src,
const buffer_s16_t& dst
) {
auto z = z_;
const auto src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while(src_p < src_end) {
const auto s0 = *__SIMD32(src_p)++;
const auto s1 = *__SIMD32(src_p)++;
const auto t0 = multiply_conjugate_s16_s32(s0, z);
const auto t1 = multiply_conjugate_s16_s32(s1, s0);
z = s1;
const int32_t theta0_int = angle_approx_0deg27(t0) * ks16;
const int32_t theta0_sat = __SSAT(theta0_int, 16);
const int32_t theta1_int = angle_approx_0deg27(t1) * ks16;
const int32_t theta1_sat = __SSAT(theta1_int, 16);
*__SIMD32(dst_p)++ = __PKHBT(
theta0_sat,
theta1_sat,
16
);
}
z_ = z;
return { dst.p, src.count, src.sampling_rate };
}
void FM::configure(const float sampling_rate, const float deviation_hz) {
/*
* angle: -pi to pi. output range: -32768 to 32767.
* Maximum delta-theta (output of atan2) at maximum deviation frequency:
* delta_theta_max = 2 * pi * deviation / sampling_rate
*/
kf = static_cast<float>(1.0f / (2.0 * pi * deviation_hz / sampling_rate));
ks16 = 32767.0f * kf;
}
}
}

View File

@ -1,75 +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.
*/
#ifndef __DSP_DEMODULATE_H__
#define __DSP_DEMODULATE_H__
#include "dsp_types.hpp"
namespace dsp {
namespace demodulate {
class AM {
public:
buffer_f32_t execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
);
private:
static constexpr float k = 1.0f / 32768.0f;
};
class SSB {
public:
buffer_f32_t execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
);
private:
static constexpr float k = 1.0f / 32768.0f;
};
class FM {
public:
buffer_f32_t execute(
const buffer_c16_t& src,
const buffer_f32_t& dst
);
buffer_s16_t execute(
const buffer_c16_t& src,
const buffer_s16_t& dst
);
void configure(const float sampling_rate, const float deviation_hz);
private:
complex16_t::rep_type z_ { 0 };
float kf { 0 };
float ks16 { 0 };
};
} /* namespace demodulate */
} /* namespace dsp */
#endif/*__DSP_DEMODULATE_H__*/

View File

@ -1,53 +0,0 @@
/*
* Copyright (C) 2015 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 "dsp_squelch.hpp"
#include <cstdint>
#include <array>
bool FMSquelch::execute(const buffer_f32_t& audio) {
if( threshold_squared == 0.0f ) {
return true;
}
// TODO: No hard-coded array size.
std::array<float, N> squelch_energy_buffer;
const buffer_f32_t squelch_energy {
squelch_energy_buffer.data(),
squelch_energy_buffer.size()
};
non_audio_hpf.execute(audio, squelch_energy);
float non_audio_max_squared = 0;
for(const auto sample : squelch_energy_buffer) {
const float sample_squared = sample * sample;
if( sample_squared > non_audio_max_squared ) {
non_audio_max_squared = sample_squared;
}
}
return (non_audio_max_squared < threshold_squared);
}
void FMSquelch::set_threshold(const float new_value) {
threshold_squared = new_value * new_value;
}

View File

@ -1,45 +0,0 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef __DSP_SQUELCH_H__
#define __DSP_SQUELCH_H__
#include "buffer.hpp"
#include "dsp_iir.hpp"
#include "dsp_iir_config.hpp"
#include <cstdint>
#include <cstddef>
class FMSquelch {
public:
bool execute(const buffer_f32_t& audio);
void set_threshold(const float new_value);
private:
static constexpr size_t N = 32;
float threshold_squared { 0.0f };
IIRBiquadFilter non_audio_hpf { non_audio_hpf_config };
};
#endif/*__DSP_SQUELCH_H__*/

View File

@ -1,117 +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.
*/
#include "event_m4.hpp"
#include "portapack_shared_memory.hpp"
#include "message_queue.hpp"
#include "ch.h"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
#include <cstdint>
#include <array>
extern "C" {
CH_IRQ_HANDLER(MAPP_IRQHandler) {
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
EventDispatcher::events_flag_isr(EVT_MASK_BASEBAND);
chSysUnlockFromIsr();
creg::m0apptxevent::clear();
CH_IRQ_EPILOGUE();
}
}
Thread* EventDispatcher::thread_event_loop = nullptr;
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);
while(is_running) {
const auto events = wait();
dispatch(events);
}
lpc43xx::creg::m0apptxevent::disable();
}
void EventDispatcher::request_stop() {
is_running = false;
}
eventmask_t EventDispatcher::wait() {
return chEvtWaitAny(ALL_EVENTS);
}
void EventDispatcher::dispatch(const eventmask_t events) {
if( events & EVT_MASK_BASEBAND ) {
handle_baseband_queue();
}
if( events & EVT_MASK_SPECTRUM ) {
handle_spectrum();
}
}
void EventDispatcher::handle_baseband_queue() {
shared_memory.baseband_queue.handle([this](Message* const message) {
this->on_message(message);
});
}
void EventDispatcher::on_message(const Message* const message) {
switch(message->id) {
case Message::ID::Shutdown:
on_message_shutdown(*reinterpret_cast<const ShutdownMessage*>(message));
break;
default:
on_message_default(message);
break;
}
}
void EventDispatcher::on_message_shutdown(const ShutdownMessage&) {
request_stop();
}
void EventDispatcher::on_message_default(const Message* const message) {
baseband_thread.on_message(message);
}
void EventDispatcher::handle_spectrum() {
const UpdateSpectrumMessage message;
baseband_thread.on_message(&message);
}

View File

@ -1,75 +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.
*/
#ifndef __EVENT_M4_H__
#define __EVENT_M4_H__
#include "event.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "message.hpp"
#include "ch.h"
constexpr auto EVT_MASK_BASEBAND = EVENT_MASK(0);
constexpr auto EVT_MASK_SPECTRUM = EVENT_MASK(1);
class EventDispatcher {
public:
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;
bool is_running = true;
eventmask_t wait();
void dispatch(const eventmask_t events);
void handle_baseband_queue();
void on_message(const Message* const message);
void on_message_shutdown(const ShutdownMessage&);
void on_message_default(const Message* const message);
void handle_spectrum();
};
#endif/*__EVENT_M4_H__*/

View File

@ -1,152 +0,0 @@
/*
* fxpt_atan2.c
*
* Copyright (C) 2012, Xo Wang
*
* Hacked up to be a bit more ARM-friendly by:
* Copyright (C) 2013 Jared Boone, ShareBrained Technology, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
/**
* Convert floating point to Q15 (1.0.15 fixed point) format.
*
* @param d floating-point value within range -1 to (1 - (2**-15)), inclusive
* @return Q15 value representing d; same range
*/
/*
static inline int16_t q15_from_double(const double d) {
return lrint(d * 32768);
}
*/
/**
* Negative absolute value. Used to avoid undefined behavior for most negative
* integer (see C99 standard 7.20.6.1.2 and footnote 265 for the description of
* abs/labs/llabs behavior).
*
* @param i 16-bit signed integer
* @return negative absolute value of i; defined for all values of i
*/
/*
static inline int16_t s16_nabs(const int16_t j) {
#if (((int16_t)-1) >> 1) == ((int16_t)-1)
// signed right shift sign-extends (arithmetic)
const int16_t negSign = ~(j >> 15); // splat sign bit into all 16 and complement
// if j is positive (negSign is -1), xor will invert j and sub will add 1
// otherwise j is unchanged
return (j ^ negSign) - negSign;
#else
return (j < 0 ? j : -j);
#endif
}
*/
/**
* Q15 (1.0.15 fixed point) multiplication. Various common rounding modes are in
* the function definition for reference (and preference).
*
* @param j 16-bit signed integer representing -1 to (1 - (2**-15)), inclusive
* @param k same format as j
* @return product of j and k, in same format
*/
static inline int16_t q15_mul(const int16_t j, const int16_t k) {
const int32_t intermediate = j * k;
#if 0 // don't round
return intermediate >> 15;
#elif 0 // biased rounding
return (intermediate + 0x4000) >> 15;
#else // unbiased rounding
return (intermediate + ((intermediate & 0x7FFF) == 0x4000 ? 0 : 0x4000)) >> 15;
#endif
}
/**
* Q15 (1.0.15 fixed point) division (non-saturating). Be careful when using
* this function, as it does not behave well when the result is out-of-range.
*
* Value is not defined if numerator is greater than or equal to denominator.
*
* @param numer 16-bit signed integer representing -1 to (1 - (2**-15))
* @param denom same format as numer; must be greater than numerator
* @return numer / denom in same format as numer and denom
*/
static inline int16_t q15_div(const int16_t numer, const int16_t denom) {
return (static_cast<int32_t>(numer) << 15) / denom;
}
/**
* 16-bit fixed point four-quadrant arctangent. Given some Cartesian vector
* (x, y), find the angle subtended by the vector and the positive x-axis.
*
* The value returned is in units of 1/65536ths of one turn. This allows the use
* of the full 16-bit unsigned range to represent a turn. e.g. 0x0000 is 0
* radians, 0x8000 is pi radians, and 0xFFFF is (65535 / 32768) * pi radians.
*
* Because the magnitude of the input vector does not change the angle it
* represents, the inputs can be in any signed 16-bit fixed-point format.
*
* @param y y-coordinate in signed 16-bit
* @param x x-coordinate in signed 16-bit
* @return angle in (val / 32768) * pi radian increments from 0x0000 to 0xFFFF
*/
static inline int16_t nabs(const int16_t j) {
//return -abs(x);
return (j < 0 ? j : -j);
}
int16_t fxpt_atan2(const int16_t y, const int16_t x) {
static const int16_t k1 = 2847;
static const int16_t k2 = 11039;
if (x == y) { // x/y or y/x would return -1 since 1 isn't representable
if (y > 0) { // 1/8
return 8192;
} else if (y < 0) { // 5/8
return 40960;
} else { // x = y = 0
return 0;
}
}
const int16_t nabs_y = nabs(y);
const int16_t nabs_x = nabs(x);
if (nabs_x < nabs_y) { // octants 1, 4, 5, 8
const int16_t y_over_x = q15_div(y, x);
const int16_t correction = q15_mul(k1, nabs(y_over_x));
const int16_t unrotated = q15_mul(k2 + correction, y_over_x);
if (x > 0) { // octants 1, 8
return unrotated;
} else { // octants 4, 5
return 32768 + unrotated;
}
} else { // octants 2, 3, 6, 7
const int16_t x_over_y = q15_div(x, y);
const int16_t correction = q15_mul(k1, nabs(x_over_y));
const int16_t unrotated = q15_mul(k2 + correction, x_over_y);
if (y > 0) { // octants 2, 3
return 16384 - unrotated;
} else { // octants 6, 7
return 49152 - unrotated;
}
}
}

View File

@ -1,29 +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.
*/
#ifndef __FXPT_ATAN2_H__
#define __FXPT_ATAN2_H__
#include <cstdint>
int16_t fxpt_atan2(const int16_t y, const int16_t x);
#endif/*__FXPT_ATAN2_H__*/

View File

@ -1,225 +0,0 @@
/*
* Copyright (C) 2015 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 <cstdint>
#include <cstddef>
#include <vector>
#include <memory>
#include "gpdma.hpp"
namespace lpc43xx {
namespace gpdma {
namespace lli {
enum class ChainType : uint8_t {
Loop = 0,
OneShot = 1,
};
enum class Interrupt : uint8_t {
All = 0,
Last = 1,
};
struct ChainConfig {
ChainType type;
size_t length;
Interrupt interrupt;
};
enum class BurstSize : uint8_t {
Transfer1 = 0,
Transfer4 = 1,
Transfer8 = 2,
Transfer16 = 3,
Transfer32 = 4,
Transfer64 = 5,
Transfer128 = 6,
Transfer256 = 7,
};
enum class TransferWidth : uint8_t {
Byte = 0,
HalfWord = 1,
Word = 2,
};
enum class Increment : uint8_t {
No = 0,
Yes = 1,
};
using PeripheralIndex = uint8_t;
struct Endpoint {
PeripheralIndex peripheral;
BurstSize burst_size;
TransferWidth transfer_size;
Increment increment;
};
struct ChannelConfig {
ChainConfig chain;
FlowControl flow_control;
Endpoint source;
Endpoint destination;
constexpr gpdma::channel::Control control(
const size_t transfer_size,
const bool last
) {
return {
.transfersize = transfer_size,
.sbsize = toUType(source.burst_size),
.dbsize = toUType(destination.burst_size),
.swidth = toUType(source.transfer_size),
.dwidth = toUType(destination.transfer_size),
.s = source_endpoint_type(flow_control),
.d = destination_endpoint_type(flow_control),
.si = toUType(source.increment),
.di = toUType(destination.increment),
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = ((chain.interrupt == Interrupt::All) || last) ? 1U : 0U,
};
}
constexpr gpdma::channel::Config config() {
return {
.e = 0,
.srcperipheral = source.peripheral,
.destperipheral = destination.peripheral,
.flowcntrl = flow_control,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
};
};
constexpr ChannelConfig channel_config_baseband_tx {
{ ChainType::Loop, 4, Interrupt::All },
gpdma::FlowControl::MemoryToPeripheral_DMAControl,
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes },
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No },
};
constexpr ChannelConfig channel_config_baseband_rx {
{ ChainType::Loop, 4, Interrupt::All },
gpdma::FlowControl::PeripheralToMemory_DMAControl,
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No },
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes },
};
constexpr ChannelConfig channel_config_audio_tx {
{ ChainType::Loop, 4, Interrupt::All },
gpdma::FlowControl::MemoryToPeripheral_DMAControl,
{ 0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes },
{ 0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::No },
};
constexpr ChannelConfig channel_config_audio_rx {
{ ChainType::Loop, 4, Interrupt::All },
gpdma::FlowControl::PeripheralToMemory_DMAControl,
{ 0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::No },
{ 0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes },
};
constexpr ChannelConfig channel_config_rssi {
{ ChainType::Loop, 4, Interrupt::All },
gpdma::FlowControl::PeripheralToMemory_DMAControl,
{ 0x0e, BurstSize::Transfer1, TransferWidth::Byte, Increment::No },
{ 0x0e, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes },
};
class Chain {
public:
using chain_t = std::vector<gpdma::channel::LLI>;
using chain_p = std::unique_ptr<chain_t>;
Chain(const ChannelConfig& cc) :
chain(std::make_unique<chain_t>(cc.chain.length))
{
set_lli_sequential(cc.chain_type);
set_source_address()...
}
private:
chain_p chain;
void set_source_peripheral(void* const address) {
set_source_address(address, 0);
}
void set_destination_peripheral(void* const address) {
set_destination_address(address, 0);
}
void set_source_address(void* const address, const size_t increment) {
size_t offset = 0;
for(auto& item : *chain) {
item.srcaddr = (uint32_t)address + offset;
offset += increment;
}
}
void set_destination_address(void* const address, const size_t increment) {
size_t offset = 0;
for(auto& item : *chain) {
item.destaddr = (uint32_t)address + offset;
offset += increment;
}
}
void set_control(const gpdma::channel::Control control) {
for(auto& item : *chain) {
item.control = control;
}
}
void set_lli_sequential(ChainType chain_type) {
for(auto& item : *chain) {
item.lli = lli_pointer(&item + 1);
}
if( chain_type == ChainType::Loop ) {
chain[chain->size() - 1].lli = lli_pointer(&chain[0]);
} else {
chain[chain->size() - 1].lli = lli_pointer(nullptr);
}
}
gpdma::channel::LLIPointer lli_pointer(const void* lli) {
return {
.lm = 0,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
}
};
} /* namespace lli */
} /* namespace gpdma */
} /* namespace lpc43xx */

View File

@ -1,313 +0,0 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
Copyright (C) 2014 Jared Boone, ShareBrained Technology
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file templates/halconf.h
* @brief HAL configuration header.
* @details HAL configuration file, this file allows to enable or disable the
* various device drivers from your application. You may also use
* this file in order to override the device drivers default settings.
*
* @addtogroup HAL_CONF
* @{
*/
#ifndef _HALCONF_H_
#define _HALCONF_H_
#include "mcuconf.h"
/**
* @brief Enables the TM subsystem.
*/
#if !defined(HAL_USE_TM) || defined(__DOXYGEN__)
#define HAL_USE_TM FALSE
#endif
/**
* @brief Enables the PAL subsystem.
*/
#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
#define HAL_USE_PAL FALSE
#endif
/**
* @brief Enables the ADC subsystem.
*/
#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
#define HAL_USE_ADC FALSE
#endif
/**
* @brief Enables the CAN subsystem.
*/
#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
#define HAL_USE_CAN FALSE
#endif
/**
* @brief Enables the EXT subsystem.
*/
#if !defined(HAL_USE_EXT) || defined(__DOXYGEN__)
#define HAL_USE_EXT FALSE
#endif
/**
* @brief Enables the GPT subsystem.
*/
#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
#define HAL_USE_GPT FALSE
#endif
/**
* @brief Enables the I2C subsystem.
*/
#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
#define HAL_USE_I2C FALSE
#endif
/**
* @brief Enables the ICU subsystem.
*/
#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
#define HAL_USE_ICU FALSE
#endif
/**
* @brief Enables the MAC subsystem.
*/
#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
#define HAL_USE_MAC FALSE
#endif
/**
* @brief Enables the MMC_SPI subsystem.
*/
#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
#define HAL_USE_MMC_SPI FALSE
#endif
/**
* @brief Enables the PWM subsystem.
*/
#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
#define HAL_USE_PWM FALSE
#endif
/**
* @brief Enables the RTC subsystem.
*/
#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
#define HAL_USE_RTC FALSE
#endif
/**
* @brief Enables the SDC subsystem.
*/
#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
#define HAL_USE_SDC FALSE
#endif
/**
* @brief Enables the SERIAL subsystem.
*/
#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
#define HAL_USE_SERIAL FALSE
#endif
/**
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
#define HAL_USE_SERIAL_USB FALSE
#endif
/**
* @brief Enables the SPI subsystem.
*/
#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
#define HAL_USE_SPI FALSE
#endif
/**
* @brief Enables the UART subsystem.
*/
#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
#define HAL_USE_UART FALSE
#endif
/**
* @brief Enables the USB subsystem.
*/
#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
#define HAL_USE_USB FALSE
#endif
/*===========================================================================*/
/* ADC driver related settings. */
/*===========================================================================*/
/**
* @brief Enables synchronous APIs.
* @note Disabling this option saves both code and data space.
*/
#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
#define ADC_USE_WAIT TRUE
#endif
/**
* @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
* @note Disabling this option saves both code and data space.
*/
#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
#define ADC_USE_MUTUAL_EXCLUSION TRUE
#endif
/*===========================================================================*/
/* CAN driver related settings. */
/*===========================================================================*/
/**
* @brief Sleep mode related APIs inclusion switch.
*/
#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
#define CAN_USE_SLEEP_MODE TRUE
#endif
/*===========================================================================*/
/* I2C driver related settings. */
/*===========================================================================*/
/**
* @brief Enables the mutual exclusion APIs on the I2C bus.
*/
#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
#define I2C_USE_MUTUAL_EXCLUSION TRUE
#endif
/*===========================================================================*/
/* MAC driver related settings. */
/*===========================================================================*/
/**
* @brief Enables an event sources for incoming packets.
*/
#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
#define MAC_USE_ZERO_COPY FALSE
#endif
/**
* @brief Enables an event sources for incoming packets.
*/
#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
#define MAC_USE_EVENTS TRUE
#endif
/*===========================================================================*/
/* MMC_SPI driver related settings. */
/*===========================================================================*/
/**
* @brief Delays insertions.
* @details If enabled this options inserts delays into the MMC waiting
* routines releasing some extra CPU time for the threads with
* lower priority, this may slow down the driver a bit however.
* This option is recommended also if the SPI driver does not
* use a DMA channel and heavily loads the CPU.
*/
#if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__)
#define MMC_NICE_WAITING TRUE
#endif
/*===========================================================================*/
/* SDC driver related settings. */
/*===========================================================================*/
/**
* @brief Number of initialization attempts before rejecting the card.
* @note Attempts are performed at 10mS intervals.
*/
#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
#define SDC_INIT_RETRY 100
#endif
/**
* @brief Include support for MMC cards.
* @note MMC support is not yet implemented so this option must be kept
* at @p FALSE.
*/
#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
#define SDC_MMC_SUPPORT FALSE
#endif
/**
* @brief Delays insertions.
* @details If enabled this options inserts delays into the MMC waiting
* routines releasing some extra CPU time for the threads with
* lower priority, this may slow down the driver a bit however.
*/
#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
#define SDC_NICE_WAITING FALSE
#endif
/*===========================================================================*/
/* SERIAL driver related settings. */
/*===========================================================================*/
/**
* @brief Default bit rate.
* @details Configuration parameter, this is the baud rate selected for the
* default configuration.
*/
#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
#define SERIAL_DEFAULT_BITRATE 38400
#endif
/**
* @brief Serial buffers size.
* @details Configuration parameter, you can change the depth of the queue
* buffers depending on the requirements of your application.
* @note The default is 64 bytes for both the transmission and receive
* buffers.
*/
#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
#define SERIAL_BUFFERS_SIZE 16
#endif
/*===========================================================================*/
/* SPI driver related settings. */
/*===========================================================================*/
/**
* @brief Enables synchronous APIs.
* @note Disabling this option saves both code and data space.
*/
#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
#define SPI_USE_WAIT TRUE
#endif
/**
* @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
* @note Disabling this option saves both code and data space.
*/
#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
#define SPI_USE_MUTUAL_EXCLUSION TRUE
#endif
#endif /* _HALCONF_H_ */
/** @} */

View File

@ -1,69 +0,0 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef __LINEAR_RESAMPLER_H__
#define __LINEAR_RESAMPLER_H__
namespace dsp {
namespace interpolation {
class LinearResampler {
public:
void configure(
const float input_rate,
const float output_rate
) {
phase_increment = calculate_increment(input_rate, output_rate);
}
template<typename InterpolatedSampleHandler>
void operator()(
const float sample,
InterpolatedSampleHandler interpolated_sample_handler
) {
const float sample_delta = sample - last_sample;
while( phase < 1.0f ) {
const float interpolated_value = last_sample + phase * sample_delta;
interpolated_sample_handler(interpolated_value);
phase += phase_increment;
}
last_sample = sample;
phase -= 1.0f;
}
void advance(const float fraction) {
phase += (fraction * phase_increment);
}
private:
float last_sample { 0.0f };
float phase { 0.0f };
float phase_increment { 0.0f };
static constexpr float calculate_increment(const float input_rate, const float output_rate) {
return input_rate / output_rate;
}
};
} /* namespace interpolation */
} /* namespace dsp */
#endif/*__LINEAR_RESAMPLER_H__*/

View File

@ -1,107 +0,0 @@
/*
* Copyright (C) 2015 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 "matched_filter.hpp"
#include <algorithm>
#include <cmath>
#include "utility.hpp"
namespace dsp {
namespace matched_filter {
void MatchedFilter::configure(
const tap_t* const taps,
const size_t taps_count,
const size_t decimation_factor
) {
samples_ = std::make_unique<samples_t>(taps_count);
taps_reversed_ = std::make_unique<taps_t>(taps_count);
taps_count_ = taps_count;
decimation_factor_ = decimation_factor;
output = 0;
std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]);
}
bool MatchedFilter::execute_once(
const sample_t input
) {
samples_[taps_count_ - decimation_factor_ + decimation_phase] = input;
advance_decimation_phase();
if( is_new_decimation_cycle() ) {
float sr_tr = 0.0f;
float si_tr = 0.0f;
float si_ti = 0.0f;
float sr_ti = 0.0f;
for(size_t n=0; n<taps_count_; n++) {
const auto sample = samples_[n];
const auto tap = taps_reversed_[n];
sr_tr += sample.real() * tap.real();
si_ti += sample.imag() * tap.imag();
si_tr += sample.imag() * tap.real();
sr_ti += sample.real() * tap.imag();
}
// N: complex multiple of samples and taps (conjugate, tap.i negated).
// P: complex multiply of samples and taps.
const auto r_n = sr_tr + si_ti;
const auto r_p = sr_tr - si_ti;
const auto i_n = si_tr - sr_ti;
const auto i_p = si_tr + sr_ti;
const auto mag_n = std::sqrt(r_n * r_n + i_n * i_n);
const auto mag_p = std::sqrt(r_p * r_p + i_p * i_p);
const auto diff = mag_p - mag_n;
output = diff;
shift_by_decimation_factor();
return true;
} else {
return false;
}
}
void MatchedFilter::shift_by_decimation_factor() {
const sample_t* s = &samples_[decimation_factor_];
sample_t* t = &samples_[0];
const size_t unroll_factor = 4;
size_t shift_count = (taps_count_ - decimation_factor_) / unroll_factor;
while(shift_count > 0) {
*t++ = *s++;
*t++ = *s++;
*t++ = *s++;
*t++ = *s++;
shift_count--;
}
shift_count = (taps_count_ - decimation_factor_) % unroll_factor;
while(shift_count > 0) {
*t++ = *s++;
shift_count--;
}
}
} /* namespace matched_filter */
} /* namespace dsp */

View File

@ -1,96 +0,0 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef __MATCHED_FILTER_H__
#define __MATCHED_FILTER_H__
#include <cstddef>
#include <complex>
#include <memory>
namespace dsp {
namespace matched_filter {
// This filter contains "magic" (optimizations) that expect the taps to
// combine a low-pass filter with a complex sinusoid that performs shifting of
// the input signal to 0Hz/DC. This also means that the taps length must be
// a multiple of the complex sinusoid period.
class MatchedFilter {
public:
using sample_t = std::complex<float>;
using tap_t = std::complex<float>;
using taps_t = tap_t[];
template<class T>
MatchedFilter(
const T& taps,
size_t decimation_factor = 1
) {
configure(taps, decimation_factor);
}
template<class T>
void configure(
const T& taps,
size_t decimation_factor
) {
configure(taps.data(), taps.size(), decimation_factor);
}
bool execute_once(const sample_t input);
float get_output() const {
return output;
}
private:
using samples_t = sample_t[];
std::unique_ptr<samples_t> samples_;
std::unique_ptr<taps_t> taps_reversed_;
size_t taps_count_ { 0 };
size_t decimation_factor_ { 1 };
size_t decimation_phase { 0 };
float output { 0 };
void shift_by_decimation_factor();
void advance_decimation_phase() {
decimation_phase = (decimation_phase + 1) % decimation_factor_;
}
bool is_new_decimation_cycle() const {
return (decimation_phase == 0);
}
void configure(
const tap_t* const taps,
const size_t taps_count,
const size_t decimation_factor
);
};
} /* namespace matched_filter */
} /* namespace dsp */
#endif/*__MATCHED_FILTER_H__*/

View File

@ -1,47 +0,0 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
Copyright (C) 2014 Jared Boone, ShareBrained Technology
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
* LPC43xx drivers configuration.
* The following settings override the default settings present in
* the various device driver implementation headers.
* Note that the settings for each driver only have effect if the whole
* driver is enabled in halconf.h.
*
* IRQ priorities:
* 7...0 Lowest...Highest.
*/
/* NOTE: Beware setting IRQ priorities < "2":
* dbg_check_enter_isr "#SV8 means that probably you have some IRQ set at a
* priority level above the kernel level (level 0 or 1 usually) so it is able
* to preempt the kernel and mess things up.
*/
/*
* DMA driver system settings.
*/
//#define LPC_ADC0_IRQ_PRIORITY 2
#define LPC_DMA_IRQ_PRIORITY 3
//#define LPC_ADC1_IRQ_PRIORITY 4
#define LPC43XX_M0APPTXEVENT_IRQ_PRIORITY 4
/* M4 is initialized by M0, which has already started PLL1 */
#define LPC43XX_M4_CLK 200000000
#define LPC43XX_M4_CLK_SRC 0x09

View File

@ -1 +0,0 @@
Experimental

View File

@ -1,93 +0,0 @@
/*
* 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.
*/
#ifndef __OOK_HPP__
#define __OOK_HPP__
#include "phase_detector.hpp"
#include "phase_accumulator.hpp"
#include <cstdint>
#include <complex>
#include <algorithm>
#include <cmath>
class OOKSlicerMagSquaredInt {
public:
using symbol_t = bool;
constexpr OOKSlicerMagSquaredInt(
const float samples_per_symbol
) : mag2_threshold_leak_factor {
static_cast<uint32_t>(
factor_sq(-1.0f / (8.0f * samples_per_symbol)) * float(1ULL << 32)
)
}
{
}
symbol_t operator()(const std::complex<int16_t> in) {
const uint32_t real2 = in.real() * in.real();
const uint32_t imag2 = in.imag() * in.imag();
const uint32_t mag2 = real2 + imag2;
const uint32_t mag2_attenuated = mag2 >> 3; // Approximation of (-4.5dB)^2
mag2_threshold = (uint64_t(mag2_threshold) * uint64_t(mag2_threshold_leak_factor)) >> 32;
mag2_threshold = std::max(mag2_threshold, mag2_attenuated);
const bool symbol = (mag2 > mag2_threshold);
return symbol;
}
private:
const uint32_t mag2_threshold_leak_factor;
uint32_t mag2_threshold = 0;
constexpr float factor_sq(float db) {
return std::pow(10.0f, db / (10.0f / 2));
}
};
class OOKClockRecovery {
public:
constexpr OOKClockRecovery(
const float samples_per_symbol
) : symbol_phase_inc_nominal { static_cast<uint32_t>(std::round((1ULL << 32) / samples_per_symbol)) },
phase_detector { samples_per_symbol },
phase_accumulator { symbol_phase_inc_nominal }
{
}
template<typename SymbolHandler>
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));
symbol_handler(detector_result.symbol);
}
}
private:
const uint32_t symbol_phase_inc_nominal;
PhaseDetectorEarlyLateGate phase_detector;
PhaseAccumulator phase_accumulator;
};
#endif/*__OOK_HPP__*/

View File

@ -1,22 +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.
*/
#include "packet_builder.hpp"

View File

@ -1,139 +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.
*/
#ifndef __PACKET_BUILDER_H__
#define __PACKET_BUILDER_H__
#include <cstdint>
#include <cstddef>
#include <bitset>
#include <functional>
#include "bit_pattern.hpp"
#include "baseband_packet.hpp"
struct NeverMatch {
bool operator()(const BitHistory&, const size_t) const {
return false;
}
};
struct FixedLength {
bool operator()(const BitHistory&, const size_t symbols_received) const {
return symbols_received >= length;
}
const size_t length;
};
template<typename PreambleMatcher, typename UnstuffMatcher, typename EndMatcher>
class PacketBuilder {
public:
using PayloadHandlerFunc = std::function<void(const baseband::Packet& packet)>;
PacketBuilder(
const PreambleMatcher preamble_matcher,
const UnstuffMatcher unstuff_matcher,
const EndMatcher end_matcher,
PayloadHandlerFunc payload_handler
) : payload_handler { std::move(payload_handler) },
preamble(preamble_matcher),
unstuff(unstuff_matcher),
end(end_matcher)
{
}
void configure(
const PreambleMatcher preamble_matcher,
const UnstuffMatcher unstuff_matcher
) {
preamble = preamble_matcher;
unstuff = unstuff_matcher;
reset_state();
}
void execute(
const uint_fast8_t symbol
) {
bit_history.add(symbol);
switch(state) {
case State::Preamble:
if( preamble(bit_history, packet.size()) ) {
state = State::Payload;
}
break;
case State::Payload:
if( !unstuff(bit_history, packet.size()) ) {
packet.add(symbol);
}
if( end(bit_history, packet.size()) ) {
// NOTE: This check is to avoid std::function nullptr check, which
// brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code.
// TODO: Make payload_handler known at compile time.
if( payload_handler ) {
packet.set_timestamp(Timestamp::now());
payload_handler(packet);
}
reset_state();
} else {
if( packet_truncated() ) {
reset_state();
}
}
break;
default:
reset_state();
break;
}
}
private:
enum State {
Preamble,
Payload,
};
bool packet_truncated() const {
return packet.size() >= packet.capacity();
}
const PayloadHandlerFunc payload_handler;
BitHistory bit_history;
PreambleMatcher preamble;
UnstuffMatcher unstuff;
EndMatcher end;
State state { State::Preamble };
baseband::Packet packet;
void reset_state() {
packet.clear();
state = State::Preamble;
}
};
#endif/*__PACKET_BUILDER_H__*/

View File

@ -1,50 +0,0 @@
/*
* 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.
*/
#ifndef __PHASE_ACCUMULATOR_HPP__
#define __PHASE_ACCUMULATOR_HPP__
#include <cstdint>
class PhaseAccumulator {
public:
constexpr PhaseAccumulator(
const uint32_t phase_inc
) : phase_inc { phase_inc }
{
}
bool operator()() {
const auto last_phase = phase;
phase += phase_inc;
return (phase < last_phase);
}
void set_inc(const uint32_t new_phase_inc) {
phase_inc = new_phase_inc;
}
private:
uint32_t phase { 0 };
uint32_t phase_inc;
};
#endif/*__PHASE_ACCUMULATOR_HPP__*/

View File

@ -1,68 +0,0 @@
/*
* 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.
*/
#ifndef __PHASE_DETECTOR_HPP__
#define __PHASE_DETECTOR_HPP__
#include <cstdint>
#include <cstddef>
#include <cmath>
class PhaseDetectorEarlyLateGate {
public:
using history_t = uint32_t;
using symbol_t = bool;
using error_t = int;
struct result_t {
symbol_t symbol;
error_t error;
};
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)) }
{
}
result_t operator()(const history_t symbol_history) const {
// 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;
return { symbol, error };
}
private:
const history_t late_mask;
const history_t early_mask;
const size_t sample_bit;
};
#endif/*__PHASE_DETECTOR_HPP__*/

View File

@ -1,72 +0,0 @@
/*
* Copyright (C) 2015 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 "proc_tpms.hpp"
#include "dsp_fir_taps.hpp"
TPMSProcessor::TPMSProcessor() {
decim_0.configure(taps_200k_decim_0.taps, 33554432);
decim_1.configure(taps_200k_decim_1.taps, 131072);
}
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;
/* 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());
}
}
for(size_t i=0; i<decim_1_out.count; i+=channel_decimation) {
const auto sliced = ook_slicer_5sps(decim_1_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);
});
ook_clock_recovery_gmc(slicer_history, [this](const bool symbol) {
this->packet_builder_ook_gmc.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);
}

View File

@ -1,125 +0,0 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef __PROC_TPMS_H__
#define __PROC_TPMS_H__
#include "baseband_processor.hpp"
#include "channel_decimator.hpp"
#include "matched_filter.hpp"
#include "clock_recovery.hpp"
#include "symbol_coding.hpp"
#include "packet_builder.hpp"
#include "baseband_packet.hpp"
#include "ook.hpp"
#include "message.hpp"
#include "portapack_shared_memory.hpp"
#include <cstdint>
#include <cstddef>
#include <bitset>
// 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 { {
{ 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 },
{ 0.0000000000e+00f, -6.2500000000e-02f }, { 4.4194173824e-02f, -4.4194173824e-02f },
{ 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 },
{ 0.0000000000e+00f, -6.2500000000e-02f }, { 4.4194173824e-02f, -4.4194173824e-02f },
} };
class TPMSProcessor : public BasebandProcessor {
public:
TPMSProcessor();
void execute(const buffer_c8_t& buffer) override;
private:
std::array<complex16_t, 512> dst;
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0;
dsp::decimate::FIRC16xR16x16Decim2 decim_1;
dsp::matched_filter::MatchedFilter mf { rect_taps_307k2_1t_p, 8 };
clock_recovery::ClockRecovery<clock_recovery::FixedErrorFilter> clock_recovery {
38400, 19200, { 0.0555f },
[this](const float symbol) { this->consume_symbol(symbol); }
};
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder {
{ 0b010101010101010101010101010110, 30, 1 },
{ },
{ 256 },
[this](const baseband::Packet& packet) {
this->payload_handler(packet);
}
};
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 };
uint32_t slicer_history { 0 };
OOKClockRecovery ook_clock_recovery_subaru {
channel_sample_rate / 8192.0f
};
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_ook_subaru {
{ 0b010101010101010101011110, 24, 0 },
{ },
{ 80 },
[](const baseband::Packet& packet) {
const TPMSPacketMessage message { tpms::SignalType::Subaru, packet };
shared_memory.application_queue.push(message);
}
};
OOKClockRecovery ook_clock_recovery_gmc {
channel_sample_rate / 8400.0f
};
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_ook_gmc {
{ 0b01010101010101010101010101100101, 32, 0 },
{ },
{ 192 },
[](const baseband::Packet& packet) {
const TPMSPacketMessage message { tpms::SignalType::GMC, packet };
shared_memory.application_queue.push(message);
}
};
void consume_symbol(const float symbol);
void payload_handler(const baseband::Packet& packet);
};
#endif/*__PROC_TPMS_H__*/

View File

@ -1,74 +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.
*/
#include "rssi.hpp"
#include <cstdint>
#include "adc.hpp"
#include "rssi_dma.hpp"
#include "utility.hpp"
#include "hal.h"
using namespace lpc43xx;
#include "hackrf_hal.hpp"
using namespace hackrf::one;
#include "portapack_adc.hpp"
namespace rf {
namespace rssi {
constexpr uint8_t adc1_sel = (1 << portapack::adc1_rssi_input);
const auto adc1_interrupt_mask = flp2(adc1_sel);
constexpr adc::CR adc1_cr {
.sel = adc1_sel,
.clkdiv = 49, /* 400kHz sample rate, 2.5us/sample @ 200MHz PCLK */
.resolution = 9, /* Ten clocks */
.edge = 0,
};
constexpr adc::Config adc1_config {
.cr = adc1_cr,
};
void init() {
adc1.clock_enable();
adc1.interrupts_disable();
adc1.power_up(adc1_config);
adc1.interrupts_enable(adc1_interrupt_mask);
dma::init();
}
void start() {
dma::enable();
adc1.start_burst();
}
void stop() {
dma::disable();
adc1.stop_burst();
}
} /* namespace rssi */
} /* namespace rf */

View File

@ -1,43 +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.
*/
#ifndef __RSSI_H__
#define __RSSI_H__
#include <cstdint>
#include <cstddef>
#include "buffer.hpp"
namespace rf {
namespace rssi {
using sample_t = uint8_t;
using buffer_t = buffer_t<sample_t>;
void init();
void start();
void stop();
} /* namespace rssi */
} /* namespace rf */
#endif/*__RSSI_H__*/

View File

@ -1,176 +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.
*/
#include "rssi_dma.hpp"
#include <cstdint>
#include <cstddef>
#include <array>
#include "hal.h"
#include "gpdma.hpp"
using namespace lpc43xx;
#include "portapack_dma.hpp"
#include "portapack_adc.hpp"
#include "thread_wait.hpp"
namespace rf {
namespace rssi {
namespace dma {
/* TODO: SO MUCH REPEATED CODE IN touch_dma.cpp!!! */
static constexpr auto& gpdma_channel = gpdma::channels[portapack::adc1_gpdma_channel_number];
constexpr uint32_t gpdma_ahb_master_peripheral = 1;
constexpr uint32_t gpdma_ahb_master_memory = 0;
constexpr uint32_t gpdma_ahb_master_lli_fetch = 0;
constexpr uint32_t gpdma_peripheral = 0xe;
constexpr uint32_t gpdma_src_peripheral = gpdma_peripheral;
constexpr uint32_t gpdma_dest_peripheral = gpdma_peripheral;
constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) {
return {
.lm = gpdma_ahb_master_lli_fetch,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
}
constexpr gpdma::channel::Control control(const size_t number_of_transfers) {
return {
.transfersize = number_of_transfers,
.sbsize = 0, /* Burst size: 1 transfer */
.dbsize = 0, /* Burst size: 1 transfer */
.swidth = 0, /* Source transfer width: byte (8 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = gpdma_ahb_master_peripheral,
.d = gpdma_ahb_master_memory,
.si = 0,
.di = 1,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
}
constexpr gpdma::channel::Config config() {
return {
.e = 0,
.srcperipheral = gpdma_src_peripheral,
.destperipheral = gpdma_dest_peripheral,
.flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
}
struct buffers_config_t {
size_t count;
size_t items_per_buffer;
};
static buffers_config_t buffers_config;
static sample_t *samples { nullptr };
static gpdma::channel::LLI *lli { nullptr };
static ThreadWait thread_wait;
static void transfer_complete() {
const auto next_lli_index = gpdma_channel.next_lli() - &lli[0];
thread_wait.wake_from_interrupt(next_lli_index);
}
static void dma_error() {
thread_wait.wake_from_interrupt(-1);
disable();
}
void init() {
gpdma_channel.set_handlers(transfer_complete, dma_error);
// LPC_GPDMA->SYNC |= (1 << gpdma_peripheral);
}
void allocate(size_t buffer_count, size_t items_per_buffer) {
buffers_config = {
.count = buffer_count,
.items_per_buffer = items_per_buffer,
};
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_ADC1->DR[portapack::adc1_rssi_input]) + 1;
const auto control_value = control(gpdma::buffer_words(buffers_config.items_per_buffer, 1));
samples = new sample_t[buffers_config.count * buffers_config.items_per_buffer];
lli = new gpdma::channel::LLI[buffers_config.count];
for(size_t i=0; i<buffers_config.count; i++) {
const auto memory = reinterpret_cast<uint32_t>(&samples[i * buffers_config.items_per_buffer]);
lli[i].srcaddr = peripheral;
lli[i].destaddr = memory;
lli[i].lli = lli_pointer(&lli[(i + 1) % buffers_config.count]);
lli[i].control = control_value;
}
}
void free() {
delete samples;
delete lli;
}
void enable() {
const auto gpdma_config = config();
gpdma_channel.configure(lli[0], gpdma_config);
gpdma_channel.enable();
}
bool is_enabled() {
return gpdma_channel.is_enabled();
}
void disable() {
gpdma_channel.disable();
}
rf::rssi::buffer_t wait_for_buffer() {
const auto next_index = thread_wait.sleep();
if( next_index >= 0 ) {
const size_t free_index = (next_index + buffers_config.count - 2) % buffers_config.count;
return { reinterpret_cast<sample_t*>(lli[free_index].destaddr), buffers_config.items_per_buffer };
} else {
// TODO: Should I return here, or loop if RDY_RESET?
return { nullptr, 0 };
}
}
} /* namespace dma */
} /* namespace rssi */
} /* namespace rf */

View File

@ -1,51 +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.
*/
#ifndef __RSSI_DMA_H__
#define __RSSI_DMA_H__
#include <cstddef>
#include "rssi.hpp"
namespace rf {
namespace rssi {
namespace dma {
using Handler = void (*)();
void init();
void allocate(size_t buffer_count, size_t items_per_buffer);
void free();
void enable();
bool is_enabled();
void disable();
rf::rssi::buffer_t wait_for_buffer();
} /* namespace dma */
} /* namespace rssi */
} /* namespace rf */
#endif/*__RSSI_DMA_H__*/

View File

@ -1,75 +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.
*/
#ifndef __RSSI_STATS_COLLECTOR_H__
#define __RSSI_STATS_COLLECTOR_H__
#include "rssi.hpp"
#include "message.hpp"
#include <cstdint>
#include <cstddef>
class RSSIStatisticsCollector {
public:
template<typename Callback>
void process(const rf::rssi::buffer_t& buffer, Callback callback) {
auto p = buffer.p;
if( p == nullptr ) {
return;
}
if( statistics.count == 0 ) {
const auto value_0 = *p;
statistics.min = value_0;
statistics.max = value_0;
}
const auto end = &p[buffer.count];
while(p < end) {
const uint32_t value = *(p++);
if( statistics.min > value ) {
statistics.min = value;
}
if( statistics.max < value ) {
statistics.max = value;
}
statistics.accumulator += value;
}
statistics.count += buffer.count;
const size_t samples_per_update = buffer.sampling_rate * update_interval;
if( statistics.count >= samples_per_update ) {
callback(statistics);
statistics.accumulator = 0;
statistics.count = 0;
}
}
private:
static constexpr float update_interval { 0.1f };
RSSIStatistics statistics;
};
#endif/*__RSSI_STATS_COLLECTOR_H__*/

View File

@ -1,63 +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.
*/
#include "rssi_thread.hpp"
#include "rssi.hpp"
#include "rssi_dma.hpp"
#include "rssi_stats_collector.hpp"
#include "message.hpp"
#include "portapack_shared_memory.hpp"
WORKING_AREA(rssi_thread_wa, 128);
Thread* RSSIThread::start(const tprio_t priority) {
return chThdCreateStatic(rssi_thread_wa, sizeof(rssi_thread_wa),
priority, ThreadBase::fn,
this
);
}
void RSSIThread::run() {
rf::rssi::init();
rf::rssi::dma::allocate(4, 400);
RSSIStatisticsCollector stats;
while(true) {
// 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 {
buffer_tmp.p, buffer_tmp.count, sampling_rate
};
stats.process(
buffer,
[](const RSSIStatistics& statistics) {
const RSSIStatisticsMessage message { statistics };
shared_memory.application_queue.push(message);
}
);
}
rf::rssi::dma::free();
}

View File

@ -1,41 +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.
*/
#ifndef __RSSI_THREAD_H__
#define __RSSI_THREAD_H__
#include "thread_base.hpp"
#include <ch.h>
#include <cstdint>
class RSSIThread : public ThreadBase {
public:
Thread* start(const tprio_t priority);
private:
void run() override;
const uint32_t sampling_rate { 400000 };
};
#endif/*__RSSI_THREAD_H__*/

View File

@ -1,70 +0,0 @@
/*
* 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.
*/
#ifndef __STREAM_INPUT_H__
#define __STREAM_INPUT_H__
#include "message.hpp"
#include "fifo.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
#include <cstdint>
#include <cstddef>
#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;
}
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;
}
private:
CaptureConfig* const config;
const size_t K;
const uint64_t event_bytes_mask;
uint64_t bytes_written = 0;
std::unique_ptr<uint8_t[]> data;
FIFO<uint8_t> fifo;
};
#endif/*__STREAM_INPUT_H__*/

View File

@ -1,44 +0,0 @@
/*
* Copyright (C) 2015 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.
*/
#ifndef __SYMBOL_CODING_H__
#define __SYMBOL_CODING_H__
#include <cstdint>
#include <cstddef>
namespace symbol_coding {
class NRZIDecoder {
public:
uint_fast8_t operator()(const uint_fast8_t symbol) {
const auto out = (~(symbol ^ last)) & 1;
last = symbol;
return out;
}
private:
uint_fast8_t last { 0 };
};
} /* namespace symbol_coding */
#endif/*__SYMBOL_CODING_H__*/

View File

@ -1,43 +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.
*/
#ifndef __THREAD_BASE_H__
#define __THREAD_BASE_H__
#include <ch.h>
class ThreadBase {
public:
virtual ~ThreadBase() = default;
protected:
static msg_t fn(void* arg) {
auto obj = static_cast<ThreadBase*>(arg);
obj->run();
return 0;
}
private:
virtual void run() = 0;
};
#endif/*__THREAD_BASE_H__*/

View File

@ -1,129 +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.
*/
#include "touch_dma.hpp"
#include <cstdint>
#include <cstddef>
#include <array>
#include "hal.h"
#include "gpdma.hpp"
using namespace lpc43xx;
#include "portapack_dma.hpp"
#include "portapack_adc.hpp"
#include "portapack_shared_memory.hpp"
namespace touch {
namespace dma {
/* TODO: SO MUCH REPEATED CODE FROM rssi_dma.cpp!!! */
static constexpr auto& gpdma_channel = gpdma::channels[portapack::adc0_gpdma_channel_number];
constexpr uint32_t gpdma_ahb_master_peripheral = 1;
constexpr uint32_t gpdma_ahb_master_memory = 0;
constexpr uint32_t gpdma_ahb_master_lli_fetch = 0;
constexpr uint32_t gpdma_src_peripheral = 0xd;
constexpr uint32_t gpdma_dest_peripheral = 0xd;
constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) {
return {
.lm = gpdma_ahb_master_lli_fetch,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
}
constexpr gpdma::channel::Control control(const size_t number_of_transfers) {
return {
.transfersize = number_of_transfers,
.sbsize = 2, /* Burst size: 8 transfers */
.dbsize = 2, /* Burst size: 8 transfers */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = gpdma_ahb_master_peripheral,
.d = gpdma_ahb_master_memory,
.si = 1,
.di = 1,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 0,
};
}
constexpr gpdma::channel::Config config() {
return {
.e = 0,
.srcperipheral = gpdma_src_peripheral,
.destperipheral = gpdma_dest_peripheral,
.flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl,
.ie = 0,
.itc = 0,
.l = 0,
.a = 0,
.h = 0,
};
}
static gpdma::channel::LLI lli;
constexpr size_t channels_per_sample = 8;
//constexpr size_t samples_per_frame = 40;
//constexpr size_t channel_samples_per_frame = channels_per_sample * samples_per_frame;
void init() {
}
void allocate() {
//samples = new sample_t[channel_samples_per_frame];
//lli = new gpdma::channel::LLI;
lli.srcaddr = reinterpret_cast<uint32_t>(&LPC_ADC0->DR[0]);
lli.destaddr = reinterpret_cast<uint32_t>(&shared_memory.touch_adc_frame.dr[0]);
lli.lli = lli_pointer(&lli);
lli.control = control(channels_per_sample);
}
void free() {
//delete samples;
//delete lli;
}
void enable() {
const auto gpdma_config = config();
gpdma_channel.configure(lli, gpdma_config);
gpdma_channel.enable();
}
bool is_enabled() {
return gpdma_channel.is_enabled();
}
void disable() {
gpdma_channel.disable();
}
} /* namespace dma */
} /* namespace touch */

View File

@ -1,52 +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.
*/
#ifndef __TOUCH_DMA_H__
#define __TOUCH_DMA_H__
#include "buffer.hpp"
#include <cstddef>
namespace touch {
namespace dma {
using sample_t = uint32_t;
using buffer_t = buffer_t<sample_t>;
using Handler = void (*)();
void init();
void allocate();
void free();
void enable();
bool is_enabled();
void disable();
buffer_t wait_for_buffer();
} /* namespace dma */
} /* namespace touch */
#endif/*__TOUCH_DMA_H__*/

View File

@ -326,6 +326,55 @@ set(MODE_CPPSRC
)
DeclareTargets(PSPE wideband_spectrum)
### Jammer
set(MODE_CPPSRC
proc_jammer.cpp
)
DeclareTargets(PJAM jammer)
### Audio transmit
set(MODE_CPPSRC
proc_audiotx.cpp
)
DeclareTargets(PATX audio_tx)
### FSK LCR
set(MODE_CPPSRC
proc_fsk_lcr.cpp
)
DeclareTargets(PLCR lcr)
### Epar
set(MODE_CPPSRC
proc_epar.cpp
)
DeclareTargets(PEPR epar)
### Play audio
set(MODE_CPPSRC
proc_playaudio.cpp
)
DeclareTargets(PPAU play_audio)
### Xylos
set(MODE_CPPSRC
proc_xylos.cpp
)
DeclareTargets(PXYL xylos)
### RDS
set(MODE_CPPSRC
proc_rds.cpp
)
DeclareTargets(PRDS rds)
### HackRF "factory" firmware
add_custom_command(

View File

@ -115,16 +115,9 @@ static void dma_error() {
void init() {
gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error);
#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
// LPC_GPDMA->SYNC |= (1 << gpdma_src_peripheral);
// LPC_GPDMA->SYNC |= (1 << gpdma_dest_peripheral);
}
void configure(
@ -167,5 +160,16 @@ baseband::buffer_t wait_for_rx_buffer() {
}
}
baseband::buffer_t wait_for_tx_buffer() {
const auto next_index = thread_wait.sleep();
if( next_index >= 0 ) {
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return { reinterpret_cast<sample_t*>(lli_loop[free_index].srcaddr), transfer_samples };
} else {
return { };
}
}
} /* namespace dma */
} /* namespace baseband */

View File

@ -31,6 +31,8 @@
namespace baseband {
namespace dma {
using Handler = void (*)();
void init();
void configure(
baseband::sample_t* const buffer_base,
@ -43,6 +45,7 @@ bool is_enabled();
void disable();
baseband::buffer_t wait_for_rx_buffer();
baseband::buffer_t wait_for_tx_buffer();
} /* namespace dma */
} /* namespace baseband */

View File

@ -24,21 +24,12 @@
#include "dsp_types.hpp"
#include "baseband.hpp"
#include "baseband_stats_collector.hpp"
#include "baseband_sgpio.hpp"
#include "baseband_dma.hpp"
#include "rssi.hpp"
#include "i2s.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_tpms.hpp"
#include "proc_ert.hpp"
#include "proc_capture.hpp"*/
using namespace lpc43xx;
#include "portapack_shared_memory.hpp"
@ -53,10 +44,12 @@ Thread* BasebandThread::thread = nullptr;
BasebandThread::BasebandThread(
uint32_t sampling_rate,
BasebandProcessor* const baseband_processor,
const tprio_t priority
const tprio_t priority,
const baseband::Direction dir
) : baseband_processor { baseband_processor },
sampling_rate { sampling_rate }
{
direction = dir;
thread = chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa),
priority, ThreadBase::fn,
this
@ -76,24 +69,40 @@ void BasebandThread::run() {
const auto baseband_buffer = std::make_unique<std::array<baseband::sample_t, 8192>>();
baseband::dma::configure(
baseband_buffer->data(),
direction()
direction
);
//baseband::dma::allocate(4, 2048);
baseband_sgpio.configure(direction());
baseband::dma::enable(direction());
baseband_sgpio.configure(direction);
baseband::dma::enable(direction);
baseband_sgpio.streaming_enable();
if (direction == baseband::Direction::Transmit) {
while( !chThdShouldTerminate() ) {
// TODO: Place correct sampling rate into buffer returned here:
const baseband::buffer_t buffer_tmp = baseband::dma::wait_for_tx_buffer();
if( buffer_tmp ) {
buffer_c8_t buffer {
buffer_tmp.p, buffer_tmp.count, sampling_rate
};
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, sampling_rate
};
if( baseband_processor ) {
baseband_processor->execute(buffer);
}
}
}
} else {
while( !chThdShouldTerminate() ) {
// TODO: Place correct sampling rate into buffer returned here:
const baseband::buffer_t buffer_tmp = baseband::dma::wait_for_rx_buffer();
if( buffer_tmp ) {
buffer_c8_t buffer {
buffer_tmp.p, buffer_tmp.count, sampling_rate
};
if( baseband_processor ) {
baseband_processor->execute(buffer);
if( baseband_processor ) {
baseband_processor->execute(buffer);
}
}
}
}
@ -102,38 +111,3 @@ void BasebandThread::run() {
baseband::dma::disable();
baseband_sgpio.streaming_disable();
}
/*
BasebandProcessor* BasebandThread::create_processor(const int32_t mode) {
switch(mode) {
case 0: return new NarrowbandAMAudio();
case 1: return new NarrowbandFMAudio();
case 2: return new WidebandFMAudio();
case 3: return new AISProcessor();
case 4: return new WidebandSpectrum();
case 5: return new TPMSProcessor();
case 6: return new ERTProcessor();
case 7: return new CaptureProcessor();
default: return nullptr;
}
}
void BasebandThread::disable() {
if( baseband_processor ) {
i2s::i2s0::tx_mute();
baseband::dma::disable();
baseband_sgpio.streaming_disable();
rf::rssi::stop();
}
}
void BasebandThread::enable() {
if( baseband_processor ) {
if( direction() == baseband::Direction::Receive ) {
rf::rssi::start();
}
baseband_sgpio.configure(direction());
baseband::dma::enable(direction());
baseband_sgpio.streaming_enable();
}
}
*/

View File

@ -33,18 +33,14 @@ public:
BasebandThread(
uint32_t sampling_rate,
BasebandProcessor* const baseband_processor,
const tprio_t priority
const tprio_t priority,
const baseband::Direction dir
);
~BasebandThread();
// This getter should die, it's just here to leak information to code that
// isn't in the right place to begin with.
baseband::Direction direction() const {
return baseband::Direction::Receive;
}
private:
static Thread* thread;
baseband::Direction direction;
BasebandProcessor* baseband_processor { nullptr };
uint32_t sampling_rate;

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