diff --git a/firmware/Makefile b/firmware/Makefile index d735d3d7..337a656d 100644 --- a/firmware/Makefile +++ b/firmware/Makefile @@ -22,6 +22,7 @@ PATH_BOOTSTRAP=bootstrap PATH_APPLICATION=application PATH_BASEBAND=baseband +PATH_BASEBAND_TX=baseband-tx TARGET=portapack-h1-firmware @@ -29,8 +30,10 @@ 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 MAKE_SPI_IMAGE=tools/make_spi_image.py +MAKE_MODULES_FILE=tools/make_baseband_file.py DFU_HACKRF=hackrf_one_usb_ram.dfu LICENSE=../LICENSE @@ -39,7 +42,10 @@ GIT_REVISION=$(shell git log -n 1 --format=%h) CP=arm-none-eabi-objcopy -all: $(TARGET).bin +MODULES = $(PATH_BASEBAND) \ + $(PATH_BASEBAND_TX) + +all: $(TARGET).bin modules release: $(TARGET).bin $(DFU_HACKRF) $(LICENSE) # TODO: Bad hack to fix location of LICENSE file for tar. @@ -48,12 +54,15 @@ release: $(TARGET).bin $(DFU_HACKRF) $(LICENSE) zip -9 -q $(TARGET)-$(GIT_REVISION).zip $(TARGET).bin $(DFU_HACKRF) LICENSE rm -f LICENSE -program: $(TARGET).bin +program: $(TARGET).bin modules dfu-util --device 1fc9:000c --download hackrf_one_usb_ram.dfu --reset sleep 1s hackrf_spiflash -w $(TARGET).bin + +modules: + $(MAKE_MODULES_FILE) $(MODULES) -$(TARGET).bin: $(MAKE_SPI_IMAGE) $(TARGET_BOOTSTRAP).bin $(TARGET_HACKRF_FIRMWARE).dfu $(TARGET_BASEBAND).bin $(TARGET_APPLICATION).bin +$(TARGET).bin: modules $(MAKE_SPI_IMAGE) $(TARGET_BOOTSTRAP).bin $(TARGET_HACKRF_FIRMWARE).dfu $(TARGET_BASEBAND).bin $(TARGET_BASEBAND_TX).bin $(TARGET_APPLICATION).bin $(MAKE_SPI_IMAGE) $(TARGET_BOOTSTRAP).bin $(TARGET_HACKRF_FIRMWARE).dfu $(TARGET_BASEBAND).bin $(TARGET_APPLICATION).bin $(TARGET).bin $(TARGET_BOOTSTRAP).bin: $(TARGET_BOOTSTRAP).elf @@ -61,12 +70,18 @@ $(TARGET_BOOTSTRAP).bin: $(TARGET_BOOTSTRAP).elf $(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 $(TARGET_APPLICATION).bin: $(TARGET_APPLICATION).elf $(CP) -O binary $(TARGET_APPLICATION).elf $(TARGET_APPLICATION).bin $(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) $(TARGET_APPLICATION).elf: always_check @$(MAKE) -s -e GIT_REVISION=$(GIT_REVISION) -C $(PATH_APPLICATION) diff --git a/firmware/application/Makefile b/firmware/application/Makefile index 38c2c6f7..2f5790a0 100755 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -149,6 +149,7 @@ CPPSRC = main.cpp \ encoder.cpp \ lcd_ili9341.cpp \ ui.cpp \ + ui_alphanum.cpp \ ui_text.cpp \ ui_widget.cpp \ ui_painter.cpp \ @@ -170,6 +171,7 @@ CPPSRC = main.cpp \ ui_console.cpp \ ui_receiver.cpp \ ui_spectrum.cpp \ + ui_loadmodule.cpp \ receiver_model.cpp \ transmitter_model.cpp \ spectrum_color_lut.cpp \ diff --git a/firmware/application/m4_startup.cpp b/firmware/application/m4_startup.cpp index 6b9df47b..5924a1fb 100644 --- a/firmware/application/m4_startup.cpp +++ b/firmware/application/m4_startup.cpp @@ -22,12 +22,14 @@ #include "m4_startup.hpp" #include "hal.h" - +#include "lpc43xx_cpp.hpp" #include "message.hpp" #include "portapack_shared_memory.hpp" #include +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? @@ -37,6 +39,7 @@ */ void m4_init(const portapack::spi_flash::region_t from, const portapack::memory::region_t to) { /* Initialize M4 code RAM */ + // DEBUG std::memcpy(reinterpret_cast(to.base()), from.base(), from.size); /* M4 core is assumed to be sleeping with interrupts off, so we can mess @@ -48,6 +51,58 @@ void m4_init(const portapack::spi_flash::region_t from, const portapack::memory: LPC_RGU->RESET_CTRL[0] = (1 << 13); } +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(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(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); +} + void m4_request_shutdown() { ShutdownMessage shutdown_message; shared_memory.baseband_queue.push(shutdown_message); diff --git a/firmware/application/m4_startup.hpp b/firmware/application/m4_startup.hpp index ce22770e..c158e3a8 100644 --- a/firmware/application/m4_startup.hpp +++ b/firmware/application/m4_startup.hpp @@ -24,10 +24,14 @@ #include +#include "ff.h" #include "memory_map.hpp" #include "spi_image.hpp" +#include "ui_navigation.hpp" void m4_init(const portapack::spi_flash::region_t from, const portapack::memory::region_t to); void m4_request_shutdown(); +void m4_switch(const char * hash); +int m4_load_image(void); #endif/*__M4_STARTUP_H__*/ diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp index ef3301e6..b1923558 100755 --- a/firmware/application/main.cpp +++ b/firmware/application/main.cpp @@ -19,8 +19,12 @@ * Boston, MA 02110-1301, USA. */ -//Reims ANFR 822519 -//TODO: UC/LC update buttons in keyboard view +//TODO: Reset baseband if module not found (instead of lockup in RAM loop) +//TODO: Module name/filename in modules.hpp to indicate requirement in case it's not found +//TODO: LCD backlight PWM +//TODO: BUG: Crash after TX stop +//TODO: Check bw setting in LCR TX +//TODO: BUG: Crash after PSN entry in RDS TX //TODO: Dynamically load baseband code depending on mode (disable M4 & interrupts, load, reset) //TODO: Bodet :) //TODO: Whistler @@ -29,14 +33,8 @@ //TODO: Persistent playdead ! //TODO: LCR EC=A,J,N //TODO: LCR full message former (see norm) -//TODO: See if receive still works -//TODO: LCR repeats -//TODO: LCR shared memory semaphore for doing/done //TODO: LCR address scan -//TODO: LCR text showing status in LCRView //TODO: AFSK NRZI -//TODO: AFSK volume -//TODO: AFSK channel bandwidth //TODO: TX power #include "ch.h" diff --git a/firmware/application/modules.h b/firmware/application/modules.h new file mode 100644 index 00000000..d9c11546 --- /dev/null +++ b/firmware/application/modules.h @@ -0,0 +1,2 @@ +const char md5_baseband[16] = {0xca,0x05,0xc3,0xbf,0x78,0x10,0xad,0xac,0x2a,0x2b,0x31,0x19,0xf9,0xe8,0x91,0x26,}; +const char md5_baseband_tx[16] = {0xe7,0x28,0x33,0x67,0x45,0x37,0x1e,0x13,0x3c,0x85,0xb5,0x91,0x51,0xaa,0xed,0x4f,}; diff --git a/firmware/application/ui_alphanum.cpp b/firmware/application/ui_alphanum.cpp new file mode 100644 index 00000000..9c433868 --- /dev/null +++ b/firmware/application/ui_alphanum.cpp @@ -0,0 +1,173 @@ +/* + * 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 "ui_alphanum.hpp" + +#include "ch.h" + +#include "ff.h" +#include "portapack.hpp" +#include "radio.hpp" + +#include "hackrf_hal.hpp" +#include "portapack_shared_memory.hpp" + +#include + +using namespace hackrf::one; + +namespace ui { + +AlphanumView::AlphanumView( + NavigationView& nav, + char txt[], + uint8_t max_len +) { + _max_len = max_len; + _lowercase = false; + + static constexpr Style style_alpha { + .font = font::fixed_8x16, + .background = Color::red(), + .foreground = Color::black(), + }; + + static constexpr Style style_num { + .font = font::fixed_8x16, + .background = Color::yellow(), + .foreground = Color::black(), + }; + + txtidx = 0; + memcpy(txtinput, txt, max_len+1); + + add_child(&text_input); + + const auto button_fn = [this](Button& button) { + this->on_button(button); + }; + + size_t n = 0; + for(auto& button : buttons) { + add_child(&button); + button.on_select = button_fn; + button.set_parent_rect({ + static_cast((n % 5) * button_w), + static_cast((n / 5) * button_h + 18), + button_w, button_h + }); + if ((n < 10) || (n == 39)) + button.set_style(&style_num); + else + button.set_style(&style_alpha); + n++; + } + set_uppercase(); + + add_child(&button_lowercase); + button_lowercase.on_select = [this, &nav, txt, max_len](Button&) { + if (_lowercase == true) { + _lowercase = false; + button_lowercase.set_text("UC"); + set_uppercase(); + } else { + _lowercase = true; + button_lowercase.set_text("LC"); + set_lowercase(); + } + }; + + add_child(&button_done); + button_done.on_select = [this, &nav, txt, max_len](Button&) { + memcpy(txt, txtinput, max_len+1); + on_changed(this->value()); + nav.pop(); + }; + + update_text(); +} + +void AlphanumView::set_uppercase() { + const char* const key_caps = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ. !<"; + + size_t n = 0; + for(auto& button : buttons) { + add_child(&button); + const std::string label { + key_caps[n] + }; + button.set_text(label); + n++; + } +} + + +void AlphanumView::set_lowercase() { + const char* const key_caps = "0123456789abcdefghijklmnopqrstuvwxyz. !<"; + + size_t n = 0; + for(auto& button : buttons) { + add_child(&button); + const std::string label { + key_caps[n] + }; + button.set_text(label); + n++; + } +} + +void AlphanumView::focus() { + button_done.focus(); +} + +char * AlphanumView::value() { + return txtinput; +} + +void AlphanumView::on_button(Button& button) { + const auto s = button.text(); + if( s == "<" ) { + char_delete(); + } else { + char_add(s[0]); + } + update_text(); +} + +void AlphanumView::char_add(const char c) { + if (txtidx < _max_len) { + txtinput[txtidx] = c; + txtidx++; + } +} + +void AlphanumView::char_delete() { + if (txtidx) { + txtidx--; + txtinput[txtidx] = ' '; + } +} + +void AlphanumView::update_text() { + text_input.set(txtinput); +} + +} diff --git a/firmware/application/ui_alphanum.hpp b/firmware/application/ui_alphanum.hpp new file mode 100644 index 00000000..1b44dab8 --- /dev/null +++ b/firmware/application/ui_alphanum.hpp @@ -0,0 +1,83 @@ +/* + * 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 "ui.hpp" +#include "ui_widget.hpp" +#include "ui_painter.hpp" +#include "ui_menu.hpp" +#include "ui_navigation.hpp" +#include "ui_font_fixed_8x16.hpp" +#include "clock_manager.hpp" +#include "message.hpp" +#include "rf_path.hpp" +#include "max2837.hpp" +#include "volume.hpp" +#include "transmitter_model.hpp" + +namespace ui { + +class AlphanumView : public View { +public: + std::function on_changed; + + AlphanumView(NavigationView& nav, char txt[], uint8_t max_len); + + void focus() override; + + char * value(); + + uint8_t txtidx; + + void char_add(const char c); + void char_delete(); + +private: + uint8_t _max_len; + bool _lowercase = false; + static constexpr size_t button_w = 240 / 5; + static constexpr size_t button_h = 28; + char txtinput[9]; + + void set_lowercase(); + void set_uppercase(); + + Text text_input { + { 88, 0, 240, 16 } + }; + + std::array buttons; + + Button button_lowercase { + { 88+64+16, 270, 32, 24 }, + "UC" + }; + + Button button_done { + { 88, 270, 64, 24 }, + "Done" + }; + + void on_button(Button& button); + + void update_text(); +}; + +} /* namespace ui */ diff --git a/firmware/application/ui_debug.cpp b/firmware/application/ui_debug.cpp index 42952a46..11cdad55 100644 --- a/firmware/application/ui_debug.cpp +++ b/firmware/application/ui_debug.cpp @@ -144,10 +144,30 @@ void DebugSDView::paint(Painter& painter) { DebugSDView::DebugSDView(NavigationView& nav) { add_children({ { &text_title, + &text_modules, &button_makefile, &button_done } }); + FIL fdst; + char buffer[256]; + uint8_t mods_version, mods_count; + UINT bw; + + const auto open_result = f_open(&fdst, "ppmods.bin", FA_OPEN_EXISTING | FA_READ); + if (open_result == FR_OK) { + f_read(&fdst, &mods_version, 1, &bw); + if (mods_version == 1) { + f_read(&fdst, &mods_count, 1, &bw); + f_read(&fdst, buffer, 8, &bw); + f_read(&fdst, buffer, 16, &bw); + buffer[16] = 0; + text_modules.set(buffer); + } else { + text_modules.set("Bad version"); + } + } + button_makefile.on_select = [this](Button&){ FATFS fs; /* Work area (file system object) for logical drives */ FIL fdst; /* File objects */ diff --git a/firmware/application/ui_debug.hpp b/firmware/application/ui_debug.hpp index fe165880..41aad942 100644 --- a/firmware/application/ui_debug.hpp +++ b/firmware/application/ui_debug.hpp @@ -155,7 +155,12 @@ public: private: Text text_title { { 32, 16, 128, 16 }, - "SD card debug", + "SD card debug" + }; + + Text text_modules { + { 8, 32, 28 * 8, 16 }, + "TESTTESTTESTTESTTESTTESTTEST" }; Button button_makefile { diff --git a/firmware/application/ui_lcr.cpp b/firmware/application/ui_lcr.cpp index d2e07780..f086a7fd 100644 --- a/firmware/application/ui_lcr.cpp +++ b/firmware/application/ui_lcr.cpp @@ -192,7 +192,8 @@ LCRView::LCRView( memset(litteral, 0, 5*8); memset(rgsb, 0, 5); - strcpy(rgsb, RGSB_list[0]); + strcpy(rgsb, RGSB_list[29]); + button_setrgsb.set_text(rgsb); add_children({ { &text_recap, @@ -282,10 +283,10 @@ LCRView::LCRView( make_frame(); shared_memory.afsk_samples_per_bit = 228000/portapack::persistent_memory::afsk_bitrate(); - shared_memory.afsk_phase_inc_mark = portapack::persistent_memory::afsk_mark_freq()*(65536*1024)/2280; - shared_memory.afsk_phase_inc_space = portapack::persistent_memory::afsk_space_freq()*(65536*1024)/2280; + shared_memory.afsk_phase_inc_mark = portapack::persistent_memory::afsk_mark_freq()*(0x10000*256)/2280; + shared_memory.afsk_phase_inc_space = portapack::persistent_memory::afsk_space_freq()*(0x10000*256)/2280; - shared_memory.afsk_fmmod = portapack::persistent_memory::afsk_bw()*33; + shared_memory.afsk_fmmod = portapack::persistent_memory::afsk_bw() * 8; memset(shared_memory.lcrdata, 0, 256); memcpy(shared_memory.lcrdata, lcrframe_f, 256); @@ -297,8 +298,10 @@ LCRView::LCRView( [this,&transmitter_model](Message* const p) { const auto message = static_cast(p); if (message->n > 0) { - char str[8] = "0... "; - str[0] = hexify(message->n); + char str[8]; + strcpy(str, to_string_dec_int(message->n).c_str()); + strcat(str, "/"); + strcat(str, to_string_dec_int(shared_memory.afsk_repeat).c_str()); text_status.set(str); } else { text_status.set("Done ! "); @@ -307,7 +310,10 @@ LCRView::LCRView( } ); - text_status.set("0... "); + char str[8]; + strcpy(str, "0/"); + strcat(str, to_string_dec_int(shared_memory.afsk_repeat).c_str()); + text_status.set(str); transmitter_model.enable(); }; diff --git a/firmware/application/ui_loadmodule.cpp b/firmware/application/ui_loadmodule.cpp new file mode 100644 index 00000000..a49ab463 --- /dev/null +++ b/firmware/application/ui_loadmodule.cpp @@ -0,0 +1,89 @@ +/* + * 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 "ui_loadmodule.hpp" + +#include "ch.h" + +#include "ff.h" +#include "hackrf_gpio.hpp" +#include "portapack.hpp" + +#include "hackrf_hal.hpp" + +#include +#include + +using namespace hackrf::one; + +namespace ui { + +void LoadModuleView::focus() { + button_ok.focus(); +} + +void LoadModuleView::paint(Painter& painter) { + +} + +void LoadModuleView::on_hide() { + auto& message_map = context().message_map(); + message_map.unregister_handler(Message::ID::ReadyForSwitch); +} + +void LoadModuleView::on_show() { + auto& message_map = context().message_map(); + message_map.register_handler(Message::ID::ReadyForSwitch, + [this](Message* const p) { + const auto message = static_cast(p); + if (m4_load_image()) { + text_info.set("Module loaded :)"); + _mod_loaded = true; + } else { + text_info.set("Module not found :("); + _mod_loaded = false; + } + } + ); + + m4_switch(_hash); +} + +LoadModuleView::LoadModuleView( + NavigationView& nav, + const char * hash, + View* new_view +) +{ + add_children({ { + &text_info, + &button_ok + } }); + + _hash = hash; + + button_ok.on_select = [this,&nav,new_view](Button&){ + nav.pop(); + if (_mod_loaded == true) nav.push(new_view); + }; +} + +} /* namespace ui */ diff --git a/firmware/application/ui_loadmodule.hpp b/firmware/application/ui_loadmodule.hpp new file mode 100644 index 00000000..f3bc99c3 --- /dev/null +++ b/firmware/application/ui_loadmodule.hpp @@ -0,0 +1,56 @@ +/* + * 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 "ui.hpp" +#include "ui_widget.hpp" +#include "ui_painter.hpp" +#include "ui_menu.hpp" +#include "ui_navigation.hpp" +#include "m4_startup.hpp" +#include "ui_font_fixed_8x16.hpp" + +namespace ui { + +class LoadModuleView : public View { +public: + LoadModuleView(NavigationView& nav, const char * hash, View* new_view); + + void on_show() override; + void on_hide() override; + void focus() override; + void paint(Painter& painter) override; + +private: + const char * _hash; + bool _mod_loaded = false; + + Text text_info { + { 8, 64, 224, 16 }, + "Searching module..." + }; + + Button button_ok { + { 88, 128, 64, 32 }, + "OK" + }; +}; + +} /* namespace ui */ diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 7713d027..67141ddf 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -35,10 +35,14 @@ #include "ui_lcr.hpp" #include "ui_whistle.hpp" #include "ui_jammer.hpp" +#include "ui_loadmodule.hpp" #include "portapack.hpp" #include "m4_startup.hpp" #include "spi_image.hpp" + +#include "modules.h" + using namespace portapack; namespace ui { @@ -104,17 +108,20 @@ void NavigationView::focus() { /* SystemMenuView ********************************************************/ SystemMenuView::SystemMenuView(NavigationView& nav) { - add_items<10>({ { - { "Play dead", ui::Color::red(), [&nav](){ nav.push(new PlayDeadView { nav, false }); } }, - { "Receiver", ui::Color::white(), [&nav](){ nav.push(new ReceiverView { nav, receiver_model }); } }, - { "Jammer", ui::Color::white(), [&nav](){ nav.push(new JammerView { nav, transmitter_model }); } }, - { "Whistle", ui::Color::white(), [&nav](){ nav.push(new WhistleView { nav, transmitter_model }); } }, - { "RDS TX", ui::Color::yellow(), [&nav](){ nav.push(new RDSView { nav, transmitter_model }); } }, - { "LCR TX", ui::Color::orange(), [&nav](){ nav.push(new LCRView { nav, transmitter_model }); } }, - { "Setup", ui::Color::white(), [&nav](){ nav.push(new SetupMenuView { nav }); } }, - { "About", ui::Color::white(), [&nav](){ nav.push(new AboutView { nav }); } }, - { "Debug", ui::Color::white(), [&nav](){ nav.push(new DebugMenuView { nav }); } }, - { "HackRF", ui::Color::white(), [&nav](){ nav.push(new HackRFFirmwareView { nav }); } }, + add_items<11>({ { + { "Play dead", ui::Color::red(), [&nav](){ nav.push(new PlayDeadView { nav, false }); } }, + { "Receiver", ui::Color::cyan(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband, new ReceiverView { nav, receiver_model } }); } }, + //{ "Nordic/BTLE RX", ui::Color::cyan(), [&nav](){ nav.push(new NotImplementedView { nav }); } }, + { "Jammer", ui::Color::white(), [&nav](){ nav.push(new JammerView { nav, transmitter_model }); } }, + { "Audio file TX", ui::Color::white(), [&nav](){ nav.push(new NotImplementedView { nav }); } }, + //{ "Encoder TX", ui::Color::green(), [&nav](){ nav.push(new NotImplementedView { nav }); } }, + { "Whistle", ui::Color::purple(), [&nav](){ nav.push(new WhistleView { nav, transmitter_model }); } }, + { "RDS TX", ui::Color::yellow(), [&nav](){ nav.push(new RDSView { nav, transmitter_model }); } }, + { "TEDI/LCR TX", ui::Color::orange(), [&nav](){ nav.push(new LCRView { nav, transmitter_model }); } }, + { "Setup", ui::Color::white(), [&nav](){ nav.push(new SetupMenuView { nav }); } }, + { "About", ui::Color::white(), [&nav](){ nav.push(new AboutView { nav }); } }, + { "Debug", ui::Color::white(), [&nav](){ nav.push(new DebugMenuView { nav }); } }, + { "HackRF", ui::Color::white(), [&nav](){ nav.push(new HackRFFirmwareView { nav }); } }, } }); } diff --git a/firmware/application/ui_rds.cpp b/firmware/application/ui_rds.cpp index ae2ac33f..86a65903 100644 --- a/firmware/application/ui_rds.cpp +++ b/firmware/application/ui_rds.cpp @@ -36,140 +36,6 @@ using namespace hackrf::one; namespace ui { - -AlphanumView::AlphanumView( - NavigationView& nav, - char txt[], - uint8_t max_len -) { - _max_len = max_len; - _lowercase = false; - - static constexpr Style style_alpha { - .font = font::fixed_8x16, - .background = Color::red(), - .foreground = Color::black(), - }; - - static constexpr Style style_num { - .font = font::fixed_8x16, - .background = Color::yellow(), - .foreground = Color::black(), - }; - - txtidx = 0; - memcpy(txtinput, txt, max_len+1); - - add_child(&text_input); - - const auto button_fn = [this](Button& button) { - this->on_button(button); - }; - - size_t n = 0; - for(auto& button : buttons) { - add_child(&button); - button.on_select = button_fn; - button.set_parent_rect({ - static_cast((n % 5) * button_w), - static_cast((n / 5) * button_h + 18), - button_w, button_h - }); - if ((n < 10) || (n == 39)) - button.set_style(&style_num); - else - button.set_style(&style_alpha); - n++; - } - set_uppercase(); - - add_child(&button_lowercase); - button_lowercase.on_select = [this, &nav, txt, max_len](Button&) { - if (_lowercase == true) { - _lowercase = false; - button_lowercase.set_text("UC"); - set_uppercase(); - } else { - _lowercase = true; - button_lowercase.set_text("LC"); - set_lowercase(); - } - }; - - add_child(&button_done); - button_done.on_select = [this, &nav, txt, max_len](Button&) { - memcpy(txt, txtinput, max_len+1); - on_changed(this->value()); - nav.pop(); - }; - - update_text(); -} - -void AlphanumView::set_uppercase() { - const char* const key_caps = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ. !<"; - - size_t n = 0; - for(auto& button : buttons) { - add_child(&button); - const std::string label { - key_caps[n] - }; - button.set_text(label); - n++; - } -} - - -void AlphanumView::set_lowercase() { - const char* const key_caps = "0123456789abcdefghijklmnopqrstuvwxyz. !<"; - - size_t n = 0; - for(auto& button : buttons) { - add_child(&button); - const std::string label { - key_caps[n] - }; - button.set_text(label); - n++; - } -} - -void AlphanumView::focus() { - button_done.focus(); -} - -char * AlphanumView::value() { - return txtinput; -} - -void AlphanumView::on_button(Button& button) { - const auto s = button.text(); - if( s == "<" ) { - char_delete(); - } else { - char_add(s[0]); - } - update_text(); -} - -void AlphanumView::char_add(const char c) { - if (txtidx < _max_len) { - txtinput[txtidx] = c; - txtidx++; - } -} - -void AlphanumView::char_delete() { - if (txtidx) { - txtidx--; - txtinput[txtidx] = ' '; - } -} - -void AlphanumView::update_text() { - text_input.set(txtinput); -} void RDSView::focus() { button_setpsn.focus(); diff --git a/firmware/application/ui_receiver.cpp b/firmware/application/ui_receiver.cpp index a4d85698..41ba3251 100644 --- a/firmware/application/ui_receiver.cpp +++ b/firmware/application/ui_receiver.cpp @@ -24,12 +24,13 @@ #include "ui_spectrum.hpp" #include "ui_console.hpp" +#include "ff.h" + #include "portapack.hpp" using namespace portapack; #include "ais_baseband.hpp" - -#include "ff.h" +#include "m4_startup.hpp" namespace ui { diff --git a/firmware/baseband-tx.bin b/firmware/baseband-tx.bin new file mode 100644 index 00000000..8b80d410 Binary files /dev/null and b/firmware/baseband-tx.bin differ diff --git a/firmware/baseband-tx/Makefile b/firmware/baseband-tx/Makefile new file mode 100755 index 00000000..9d104eab --- /dev/null +++ b/firmware/baseband-tx/Makefile @@ -0,0 +1,275 @@ +# +# 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_m4.cpp \ + irq_ipc_m4.cpp \ + gpdma.cpp \ + baseband_dma.cpp \ + portapack_shared_memory.cpp \ + baseband_processor.cpp \ + channel_decimator.cpp \ + dsp_decimate.cpp \ + dsp_demodulate.cpp \ + matched_filter.cpp \ + proc_jammer.cpp \ + proc_fsk_lcr.cpp \ + dsp_squelch.cpp \ + clock_recovery.cpp \ + packet_builder.cpp \ + dsp_fft.cpp \ + dsp_fir_taps.cpp \ + fxpt_atan2.cpp \ + rssi.cpp \ + rssi_dma.cpp \ + audio.cpp \ + audio_dma.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 = 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 diff --git a/firmware/baseband-tx/audio_dma.cpp b/firmware/baseband-tx/audio_dma.cpp new file mode 100644 index 00000000..6fa6439b --- /dev/null +++ b/firmware/baseband-tx/audio_dma.cpp @@ -0,0 +1,238 @@ +/* + * 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 +#include +#include + +#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(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 buffer_tx; +static std::array buffer_rx; + +static std::array lli_tx_loop; +static std::array 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(&LPC_I2S0->TXFIFO); + const auto control_value = control_tx(transfer_bytes); + for(size_t i=0; i(&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(&LPC_I2S0->RXFIFO); + const auto control_value = control_rx(transfer_bytes); + for(size_t i=0; i(&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_force(); + gpdma_channel_i2s0_rx.disable_force(); +} + +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(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(lli_rx_loop[free_index].srcaddr), transfer_samples }; + } else { + return { nullptr, 0 }; + } +} + +} /* namespace dma */ +} /* namespace audio */ diff --git a/firmware/baseband-tx/audio_dma.hpp b/firmware/baseband-tx/audio_dma.hpp new file mode 100644 index 00000000..ec85f080 --- /dev/null +++ b/firmware/baseband-tx/audio_dma.hpp @@ -0,0 +1,43 @@ +/* + * 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 + +#include "audio.hpp" + +namespace audio { +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__*/ diff --git a/firmware/baseband-tx/audio_stats_collector.hpp b/firmware/baseband-tx/audio_stats_collector.hpp new file mode 100644 index 00000000..189fff80 --- /dev/null +++ b/firmware/baseband-tx/audio_stats_collector.hpp @@ -0,0 +1,95 @@ +/* + * 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 "buffer.hpp" +#include "message.hpp" +#include "utility.hpp" + +#include +#include + +class AudioStatsCollector { +public: + template + void feed(buffer_s16_t src, Callback callback) { + consume_audio_buffer(src); + + if( update_stats(src.count, src.sampling_rate) ) { + callback(statistics); + } + } + + template + void mute(const size_t sample_count, const size_t sampling_rate, Callback callback) { + if( update_stats(sample_count, sampling_rate) ) { + callback(statistics); + } + } + +private: + static constexpr float update_interval { 0.1f }; + uint64_t squared_sum { 0 }; + uint32_t max_squared { 0 }; + size_t count { 0 }; + + AudioStatistics statistics; + + void consume_audio_buffer(buffer_s16_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 uint64_t sample_squared = sample * sample; + squared_sum += sample_squared; + if( sample_squared > max_squared ) { + max_squared = sample_squared; + } + } + } + + bool 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 ) { + const float squared_sum_f = squared_sum; + const float max_squared_f = max_squared; + const float squared_avg_f = squared_sum_f / count; + statistics.rms_db = complex16_mag_squared_to_dbv_norm(squared_avg_f); + statistics.max_db = complex16_mag_squared_to_dbv_norm(max_squared_f); + statistics.count = count; + + squared_sum = 0; + max_squared = 0; + count = 0; + + return true; + } else { + return false; + } + } +}; + +#endif/*__AUDIO_STATS_COLLECTOR_H__*/ diff --git a/firmware/baseband-tx/baseband_dma.cpp b/firmware/baseband-tx/baseband_dma.cpp new file mode 100644 index 00000000..aa119dc6 --- /dev/null +++ b/firmware/baseband-tx/baseband_dma.cpp @@ -0,0 +1,203 @@ +/* + * 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 "portapack_shared_memory.hpp" + +#include +#include +#include + +#include "hal.h" +#include "gpdma.hpp" + +using namespace lpc43xx; + +#include "portapack_dma.hpp" + +namespace baseband { +namespace dma { + + int quitt = 0; + +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(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 lli_loop; +static constexpr auto& gpdma_channel_sgpio = gpdma::channels[portapack::sgpio_gpdma_channel_number]; + +//static Mailbox mailbox; +//static std::array messages; +static Semaphore semaphore; + +static volatile const gpdma::channel::LLI* next_lli = nullptr; + +void transfer_complete() { + next_lli = gpdma_channel_sgpio.next_lli(); + quitt = 0; + /* TODO: Is Mailbox the proper synchronization mechanism for this? */ + //chMBPostI(&mailbox, 0); + chSemSignalI(&semaphore); +} + +static void dma_error() { + disable(); +} + +void init() { + //chMBInit(&mailbox, messages.data(), messages.size()); + chSemInit(&semaphore, 0); + 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(&LPC_SGPIO->REG_SS[0]); + const auto control_value = control(direction, gpdma::buffer_words(transfer_bytes, 4)); + for(size_t i=0; i(&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); + + //chMBReset(&mailbox); + chSemReset(&semaphore, 0); + + gpdma_channel_sgpio.enable(); +} + +bool is_enabled() { + return gpdma_channel_sgpio.is_enabled(); +} + +void disable() { + gpdma_channel_sgpio.disable_force(); +} + +baseband::buffer_t wait_for_rx_buffer() { + //msg_t msg; + //const auto status = chMBFetch(&mailbox, &msg, TIME_INFINITE); + const auto status = chSemWait(&semaphore); + if (quitt) return { nullptr, 0 }; + if( status == RDY_OK ) { + const auto next = next_lli; + if( next ) { + const size_t next_index = next - &lli_loop[0]; + const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask; + return { reinterpret_cast(lli_loop[free_index].destaddr), transfer_samples }; + } else { + return { nullptr, 0 }; + } + } else { + return { nullptr, 0 }; + } +} + +baseband::buffer_t wait_for_tx_buffer() { + //msg_t msg; + //const auto status = chMBFetch(&mailbox, &msg, TIME_INFINITE); + const auto status = chSemWait(&semaphore); + if( status == RDY_OK ) { + const auto next = next_lli; + if( next ) { + const size_t next_index = next - &lli_loop[0]; + const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask; + return { reinterpret_cast(lli_loop[free_index].srcaddr), transfer_samples }; + } else { + return { nullptr, 0 }; + } + } else { + return { nullptr, 0 }; + } +} + +} /* namespace dma */ +} /* namespace baseband */ diff --git a/firmware/baseband-tx/baseband_dma.hpp b/firmware/baseband-tx/baseband_dma.hpp new file mode 100644 index 00000000..99e32de8 --- /dev/null +++ b/firmware/baseband-tx/baseband_dma.hpp @@ -0,0 +1,53 @@ +/* + * 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 +#include + +#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__*/ diff --git a/firmware/baseband-tx/baseband_processor.cpp b/firmware/baseband-tx/baseband_processor.cpp new file mode 100644 index 00000000..83e6d857 --- /dev/null +++ b/firmware/baseband-tx/baseband_processor.cpp @@ -0,0 +1,122 @@ +/* + * 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 "dsp_fft.hpp" + +#include "audio_dma.hpp" + +#include "message.hpp" +#include "event_m4.hpp" +#include "utility.hpp" + +#include +#include + +void BasebandProcessor::update_spectrum() { + // Called from idle thread (after EVT_MASK_SPECTRUM is flagged) + if( channel_spectrum_request_update ) { + /* Decimated buffer is full. Compute spectrum. */ + channel_spectrum_request_update = false; + fft_c_preswapped(channel_spectrum); + + ChannelSpectrumMessage spectrum_message; + for(size_t i=0; i .magnitude, or something more (less!) accurate. */ + spectrum_message.spectrum.db_count = spectrum_message.spectrum.db.size(); + spectrum_message.spectrum.sampling_rate = channel_spectrum_sampling_rate; + spectrum_message.spectrum.channel_filter_pass_frequency = channel_filter_pass_frequency; + spectrum_message.spectrum.channel_filter_stop_frequency = channel_filter_stop_frequency; + shared_memory.application_queue.push(spectrum_message); + } +} + +void BasebandProcessor::feed_channel_stats(const buffer_c16_t channel) { + channel_stats.feed( + channel, + [this](const ChannelStatistics statistics) { + this->post_channel_stats_message(statistics); + } + ); +} + +void BasebandProcessor::feed_channel_spectrum( + const buffer_c16_t channel, + const uint32_t filter_pass_frequency, + const uint32_t filter_stop_frequency +) { + channel_filter_pass_frequency = filter_pass_frequency; + channel_filter_stop_frequency = filter_stop_frequency; + channel_spectrum_decimator.feed( + channel, + [this](const buffer_c16_t data) { + this->post_channel_spectrum_message(data); + } + ); +} + +void BasebandProcessor::fill_audio_buffer(const buffer_s16_t audio) { + auto audio_buffer = audio::dma::tx_empty_buffer();; + for(size_t i=0; ipost_audio_stats_message(statistics); + } + ); +} + +void BasebandProcessor::post_audio_stats_message(const AudioStatistics statistics) { + audio_stats_message.statistics = statistics; + shared_memory.application_queue.push(audio_stats_message); +} diff --git a/firmware/baseband-tx/baseband_processor.hpp b/firmware/baseband-tx/baseband_processor.hpp new file mode 100644 index 00000000..a1a22889 --- /dev/null +++ b/firmware/baseband-tx/baseband_processor.hpp @@ -0,0 +1,76 @@ +/* + * 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 "complex.hpp" + +#include "block_decimator.hpp" +#include "channel_stats_collector.hpp" +#include "audio_stats_collector.hpp" + +#include +#include +#include + +class BasebandProcessor { +public: + virtual ~BasebandProcessor() = default; + + virtual void execute(buffer_c8_t buffer) = 0; + + void update_spectrum(); + +protected: + void feed_channel_stats(const buffer_c16_t channel); + + void feed_channel_spectrum( + const buffer_c16_t channel, + const uint32_t filter_pass_frequency, + const uint32_t filter_stop_frequency + ); + + void fill_audio_buffer(const buffer_s16_t audio); + + volatile bool channel_spectrum_request_update { false }; + std::array, 256> channel_spectrum; + uint32_t channel_spectrum_sampling_rate { 0 }; + uint32_t channel_filter_pass_frequency { 0 }; + uint32_t channel_filter_stop_frequency { 0 }; + +private: + BlockDecimator<256> channel_spectrum_decimator { 4 }; + + ChannelStatsCollector channel_stats; + ChannelStatisticsMessage channel_stats_message; + + AudioStatsCollector audio_stats; + AudioStatisticsMessage audio_stats_message; + + void post_channel_stats_message(const ChannelStatistics statistics); + void post_channel_spectrum_message(const buffer_c16_t data); + void feed_audio_stats(const buffer_s16_t audio); + void post_audio_stats_message(const AudioStatistics statistics); +}; + +#endif/*__BASEBAND_PROCESSOR_H__*/ diff --git a/firmware/baseband-tx/baseband_stats_collector.hpp b/firmware/baseband-tx/baseband_stats_collector.hpp new file mode 100644 index 00000000..a44c2936 --- /dev/null +++ b/firmware/baseband-tx/baseband_stats_collector.hpp @@ -0,0 +1,96 @@ +/* + * 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 "utility_m4.hpp" + +#include +#include + +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 + void process(buffer_c8_t buffer, Callback callback) { + samples += buffer.count; + + const size_t report_samples = buffer.sampling_rate * report_interval; + const auto report_delta = samples - samples_last_report; + if( report_delta >= report_samples ) { + 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 = m4_flag_saturation(); + clear_m4_flag_saturation(); + + callback(statistics); + + samples_last_report = samples; + } + } + +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 }; +}; + +#endif/*__BASEBAND_STATS_COLLECTOR_H__*/ diff --git a/firmware/baseband-tx/block_decimator.hpp b/firmware/baseband-tx/block_decimator.hpp new file mode 100644 index 00000000..dbca0ebd --- /dev/null +++ b/firmware/baseband-tx/block_decimator.hpp @@ -0,0 +1,100 @@ +/* + * 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 +#include +#include + +#include "dsp_types.hpp" +#include "complex.hpp" + +template +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 + void feed(const buffer_c16_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 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__*/ diff --git a/firmware/baseband-tx/channel_decimator.cpp b/firmware/baseband-tx/channel_decimator.cpp new file mode 100644 index 00000000..a7e7edf7 --- /dev/null +++ b/firmware/baseband-tx/channel_decimator.cpp @@ -0,0 +1,84 @@ +/* + * 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(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[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[1024], [-32768, 32512] */ + const auto stage_0_out = translate.execute(buffer, work_baseband_buffer); + + //if( fs_over_4_downconvert ) { + // // TODO: + //} else { + // Won't work until cic_0 will accept input type of buffer_c8_t. + // stage_0_out = cic_0.execute(buffer, work_baseband_buffer); + //} + + /* 1.536MHz complex[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 8 + * -> decimation by 2 + * -> 768kHz complex[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[512], [-32768, 32512] + * -> 3rd order CIC decimation by 2, gain of 1 + * -> 384kHz complex[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[256], [-32768, 32512] + * -> 3rd order CIC decimation by 2, gain of 1 + * -> 192kHz complex[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[128], [-32768, 32512] + * -> 3rd order CIC decimation by 2, gain of 1 + * -> 96kHz complex[64], [-32768, 32512] */ + auto cic_4_out = cic_4.execute(cic_3_out, work_baseband_buffer); + + return cic_4_out; +} diff --git a/firmware/baseband-tx/channel_decimator.hpp b/firmware/baseband-tx/channel_decimator.hpp new file mode 100644 index 00000000..3e20c0df --- /dev/null +++ b/firmware/baseband-tx/channel_decimator.hpp @@ -0,0 +1,79 @@ +/* + * 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 + +class ChannelDecimator { +public: + enum class DecimationFactor { + By4, + By8, + By16, + By32, + }; + + constexpr ChannelDecimator( + ) : decimation_factor { DecimationFactor::By32 } + { + } + + constexpr ChannelDecimator( + const DecimationFactor decimation_factor + ) : decimation_factor { decimation_factor } + { + } + + void set_decimation_factor(const DecimationFactor f) { + decimation_factor = f; + } + + buffer_c16_t execute(buffer_c8_t buffer) { + auto decimated = execute_decimation(buffer); + + return decimated; + } + +private: + std::array work_baseband; + + //const bool fs_over_4_downconvert = true; + + dsp::decimate::TranslateByFSOver4AndDecimateBy2CIC3 translate; + //dsp::decimate::DecimateBy2CIC3 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; + + buffer_c16_t execute_decimation(buffer_c8_t buffer); +}; + +#endif/*__CHANNEL_DECIMATOR_H__*/ diff --git a/firmware/baseband-tx/channel_stats_collector.hpp b/firmware/baseband-tx/channel_stats_collector.hpp new file mode 100644 index 00000000..8c91ac5b --- /dev/null +++ b/firmware/baseband-tx/channel_stats_collector.hpp @@ -0,0 +1,66 @@ +/* + * 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 +#include + +#include + +class ChannelStatsCollector { +public: + template + void feed(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 = complex16_mag_squared_to_dbv_norm(max_squared_f); + 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__*/ diff --git a/firmware/baseband-tx/chconf.h b/firmware/baseband-tx/chconf.h new file mode 100755 index 00000000..8045cf4f --- /dev/null +++ b/firmware/baseband-tx/chconf.h @@ -0,0 +1,546 @@ +/* + 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 TRUE +#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_ */ + +/** @} */ diff --git a/firmware/baseband-tx/clock_recovery.cpp b/firmware/baseband-tx/clock_recovery.cpp new file mode 100644 index 00000000..17cb5c70 --- /dev/null +++ b/firmware/baseband-tx/clock_recovery.cpp @@ -0,0 +1,22 @@ +/* + * 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" diff --git a/firmware/baseband-tx/clock_recovery.hpp b/firmware/baseband-tx/clock_recovery.hpp new file mode 100644 index 00000000..bd17255d --- /dev/null +++ b/firmware/baseband-tx/clock_recovery.hpp @@ -0,0 +1,178 @@ +/* + * 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 +#include +#include + +#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 + 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 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 +class ClockRecovery { +public: + ClockRecovery( + const float sampling_rate, + const float symbol_rate, + ErrorFilter error_filter, + std::function symbol_handler + ) : symbol_handler { symbol_handler } + { + configure(sampling_rate, symbol_rate, error_filter); + } + + ClockRecovery( + std::function symbol_handler + ) : symbol_handler { 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; + std::function 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) { + symbol_handler(symbol); + + const float adjustment = error_filter(lateness); + resampler.advance(adjustment); + } +}; + +} /* namespace clock_recovery */ + +#endif/*__CLOCK_RECOVERY_H__*/ diff --git a/firmware/baseband-tx/description b/firmware/baseband-tx/description new file mode 100644 index 00000000..f2606500 --- /dev/null +++ b/firmware/baseband-tx/description @@ -0,0 +1 @@ +More specific stuff for testing :o diff --git a/firmware/baseband-tx/dsp_decimate.cpp b/firmware/baseband-tx/dsp_decimate.cpp new file mode 100644 index 00000000..e8fd1824 --- /dev/null +++ b/firmware/baseband-tx/dsp_decimate.cpp @@ -0,0 +1,403 @@ +/* + * 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 + +namespace dsp { +namespace decimate { + +buffer_c16_t TranslateByFSOver4AndDecimateBy2CIC3::execute(buffer_c8_t src, buffer_c16_t dst) { + /* Translates incoming complex 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(&src.p[0]); + uint32_t* const src_end = reinterpret_cast(&src.p[src.count]); + uint32_t* dst_p = reinterpret_cast(&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( + buffer_c16_t src, + 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; + uint32_t t3, t4; + const uint32_t taps = 0x00000003; + auto s = src.p; + auto d = dst.p; + const auto d_end = &dst.p[src.count / 2]; + uint32_t i, q; + while(d < d_end) { + i = __SXTH(t1, 0); /* 1: I0 */ + q = __SXTH(t1, 16); /* 1: Q0 */ + i = __SMLABB(t2, taps, i); /* 1: I1*3 + I0 */ + q = __SMLATB(t2, taps, q); /* 1: Q1*3 + Q0 */ + + t3 = *__SIMD32(s)++; /* 3: Q2:I2 */ + 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 }; +} + +buffer_s16_t FIR64AndDecimateBy2Real::execute( + buffer_s16_t src, + 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 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 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( + buffer_s16_t src, + 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 }; +} +#if 0 +buffer_c16_t DecimateBy2HBF5Complex::execute( + buffer_c16_t const src, + buffer_c16_t const 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] = z[5]; + z[4] = z[6]; + z[5] = z[7]; + z[6] = z[8]; + z[7] = z[9]; + z[8] = z[10]; + z[9] = *(src_p++); + z[10] = *(src_p++); + + int32_t t_real { z[5].real * 256 }; + int32_t t_imag { z[5].imag * 256 }; + t_real += (z[ 0].real + z[10].real) * 3; + t_imag += (z[ 0].imag + z[10].imag) * 3; + t_real -= (z[ 2].real + z[ 8].real) * 25; + t_imag -= (z[ 2].imag + z[ 8].imag) * 25; + t_real += (z[ 4].real + z[ 6].real) * 150; + t_imag += (z[ 4].imag + z[ 6].imag) * 150; + *(dst_p++) = { t_real / 256, t_imag / 256 }; + } + + return { dst.p, src.count / 2, src.sampling_rate / 2 }; +} + +buffer_c16_t DecimateBy2HBF7Complex::execute( + buffer_c16_t const src, + buffer_c16_t const 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] = z[5]; + z[4] = z[6]; + z[5] = z[7]; + z[6] = z[8]; + z[7] = z[9]; + z[8] = z[10]; + z[9] = *(src_p++); + z[10] = *(src_p++); + + int32_t t_real { z[5].real * 512 }; + int32_t t_imag { z[5].imag * 512 }; + t_real += (z[ 0].real + z[10].real) * 7; + t_imag += (z[ 0].imag + z[10].imag) * 7; + t_real -= (z[ 2].real + z[ 8].real) * 53; + t_imag -= (z[ 2].imag + z[ 8].imag) * 53; + t_real += (z[ 4].real + z[ 6].real) * 302; + t_imag += (z[ 4].imag + z[ 6].imag) * 302; + *(dst_p++) = { t_real / 512, t_imag / 512 }; + } + + return { dst.p, src.count / 2, src.sampling_rate / 2 }; +} +#endif +} /* namespace decimate */ +} /* namespace dsp */ diff --git a/firmware/baseband-tx/dsp_decimate.hpp b/firmware/baseband-tx/dsp_decimate.hpp new file mode 100644 index 00000000..9cc90b88 --- /dev/null +++ b/firmware/baseband-tx/dsp_decimate.hpp @@ -0,0 +1,242 @@ +/* + * 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 +#include +#include +#include + +#include "utility.hpp" + +#include "dsp_types.hpp" + +namespace dsp { +namespace decimate { + +class TranslateByFSOver4AndDecimateBy2CIC3 { +public: + buffer_c16_t execute( + buffer_c8_t src, + buffer_c16_t dst + ); + +private: + uint32_t _q1_i0 { 0 }; + uint32_t _q0_i1 { 0 }; +}; + +class DecimateBy2CIC3 { +public: + buffer_c16_t execute( + buffer_c16_t src, + buffer_c16_t dst + ); + +private: + uint32_t _iq0 { 0 }; + uint32_t _iq1 { 0 }; +}; + +class FIR64AndDecimateBy2Real { +public: + static constexpr size_t taps_count = 64; + + FIR64AndDecimateBy2Real( + const std::array& taps + ) : taps(taps) + { + } + + buffer_s16_t execute( + buffer_s16_t src, + buffer_s16_t dst + ); + +private: + std::array z; + const std::array& taps; +}; + +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 + void configure( + const T& taps, + const size_t decimation_factor + ) { + samples_ = std::make_unique(taps.size()); + taps_reversed_ = std::make_unique(taps.size()); + taps_count_ = taps.size(); + decimation_factor_ = decimation_factor; + std::reverse_copy(taps.cbegin(), taps.cend(), &taps_reversed_[0]); + } + + buffer_c16_t execute( + buffer_c16_t src, + buffer_c16_t dst + ); + +private: + using samples_t = sample_t[]; + + std::unique_ptr samples_; + std::unique_ptr taps_reversed_; + size_t taps_count_; + size_t decimation_factor_; +}; + +class DecimateBy2CIC4Real { +public: + buffer_s16_t execute( + buffer_s16_t src, + buffer_s16_t dst + ); + +private: + int16_t z[5]; +}; +#if 0 +class DecimateBy2HBF5Complex { +public: + buffer_c16_t execute( + buffer_c16_t const src, + buffer_c16_t const dst + ); + +private: + complex16_t z[11]; +}; + +class DecimateBy2HBF7Complex { +public: + buffer_c16_t execute( + buffer_c16_t const src, + buffer_c16_t const dst + ); + +private: + complex16_t z[11]; +}; +#endif +/* From http://www.dspguru.com/book/export/html/3 + +Here are several basic techniques to fake circular buffers: + +Split the calculation: You can split any FIR calculation into its "pre-wrap" +and "post-wrap" parts. By splitting the calculation into these two parts, you +essentially can do the circular logic only once, rather than once per tap. +(See fir_double_z in FirAlgs.c above.) + +Duplicate the delay line: For a FIR with N taps, use a delay line of size 2N. +Copy each sample to its proper location, as well as at location-plus-N. +Therefore, the FIR calculation's MAC loop can be done on a flat buffer of N +points, starting anywhere within the first set of N points. The second set of +N delayed samples provides the "wrap around" comparable to a true circular +buffer. (See fir_double_z in FirAlgs.c above.) + +Duplicate the coefficients: This is similar to the above, except that the +duplication occurs in terms of the coefficients, not the delay line. +Compared to the previous method, this has a calculation advantage of not +having to store each incoming sample twice, and it also has a memory +advantage when the same coefficient set will be used on multiple delay lines. +(See fir_double_h in FirAlgs.c above.) + +Use block processing: In block processing, you use a delay line which is a +multiple of the number of taps. You therefore only have to move the data +once per block to implement the delay-line mechanism. When the block size +becomes "large", the overhead of a moving the delay line once per block +becomes negligible. +*/ + +#if 0 +template +class FIRAndDecimateBy2Complex { +public: + FIR64AndDecimateBy2Complex( + const std::array& taps + ) : taps { taps } + { + } + + buffer_c16_t execute( + buffer_c16_t const src, + buffer_c16_t const 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. + */ + + return { dst.p, src.count / 2 }; + } + +private: + std::array z; + const std::array& taps; + + complex process_one(const size_t start_offset) { + const auto split = &z[start_offset]; + const auto end = &z[z.size()]; + auto tap = &taps[0]; + + complex t { 0, 0 }; + + auto p = split; + while(p < end) { + const auto t = *(tap++); + const auto c = *(p++); + t.real += c.real * t; + t.imag += c.imag * t; + } + + p = &z[0]; + while(p < split) { + const auto t = *(tap++); + const auto c = *(p++); + t.real += c.real * t; + t.imag += c.imag * t; + } + + return { t.real / 65536, t.imag / 65536 }; + } +}; +#endif +} /* namespace decimate */ +} /* namespace dsp */ + +#endif/*__DSP_DECIMATE_H__*/ diff --git a/firmware/baseband-tx/dsp_demodulate.cpp b/firmware/baseband-tx/dsp_demodulate.cpp new file mode 100644 index 00000000..4ee6ed68 --- /dev/null +++ b/firmware/baseband-tx/dsp_demodulate.cpp @@ -0,0 +1,111 @@ +/* + * 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 + +namespace dsp { +namespace demodulate { + +buffer_s16_t AM::execute( + buffer_c16_t src, + buffer_s16_t dst +) { + /* Intermediate maximum value: 46341 (when input is -32768,-32768). */ + /* Normalized to maximum 32767 for int16_t representation. */ + + 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 s = *(src_p++); + // const uint32_t r_sq = s.real() * s.real(); + // const uint32_t i_sq = s.imag() * s.imag(); + // const uint32_t mag_sq = r_sq + i_sq; + 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); + const int32_t mag0_int = __builtin_sqrtf(mag_sq0); + const int32_t mag0_sat = __SSAT(mag0_int, 16); + const int32_t mag1_int = __builtin_sqrtf(mag_sq1); + const int32_t mag1_sat = __SSAT(mag1_int, 16); + *__SIMD32(dst_p)++ = __PKHBT( + mag0_sat, + mag1_sat, + 16 + ); + } + + return { dst.p, src.count, src.sampling_rate }; +} +/* +static inline float angle_approx_4deg0(const complex32_t t) { + const auto x = static_cast(t.imag()) / static_cast(t.real()); + return 16384.0f * x; +} +*/ +static inline float angle_approx_0deg27(const complex32_t t) { + const auto x = static_cast(t.imag()) / static_cast(t.real()); + return x / (1.0f + 0.28086f * x * x); +} +/* +static inline float angle_precise(const complex32_t t) { + return atan2f(t.imag(), t.real()); +} +*/ +buffer_s16_t FM::execute( + buffer_c16_t src, + 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) * k; + const int32_t theta0_sat = __SSAT(theta0_int, 16); + const int32_t theta1_int = angle_approx_0deg27(t1) * k; + 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 }; +} + +} +} diff --git a/firmware/baseband-tx/dsp_demodulate.hpp b/firmware/baseband-tx/dsp_demodulate.hpp new file mode 100644 index 00000000..c5f871e1 --- /dev/null +++ b/firmware/baseband-tx/dsp_demodulate.hpp @@ -0,0 +1,70 @@ +/* + * 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_s16_t execute( + buffer_c16_t src, + buffer_s16_t dst + ); +}; + +class FM { +public: + /* + * 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 + */ + constexpr FM( + const float sampling_rate, + const float deviation_hz + ) : z_ { 0 }, + k { static_cast(32767.0f / (2.0 * pi * deviation_hz / sampling_rate)) } + { + } + + buffer_s16_t execute( + buffer_c16_t src, + buffer_s16_t dst + ); + + void configure(const float sampling_rate, const float deviation_hz) { + k = static_cast(32767.0f / (2.0 * pi * deviation_hz / sampling_rate)); + } + +private: + complex16_t::rep_type z_; + float k; +}; + +} /* namespace demodulate */ +} /* namespace dsp */ + +#endif/*__DSP_DEMODULATE_H__*/ diff --git a/firmware/baseband-tx/dsp_fir_taps.cpp b/firmware/baseband-tx/dsp_fir_taps.cpp new file mode 100644 index 00000000..b8bed3f2 --- /dev/null +++ b/firmware/baseband-tx/dsp_fir_taps.cpp @@ -0,0 +1,22 @@ +/* + * 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_fir_taps.hpp" diff --git a/firmware/baseband-tx/dsp_fir_taps.hpp b/firmware/baseband-tx/dsp_fir_taps.hpp new file mode 100644 index 00000000..9839a9de --- /dev/null +++ b/firmware/baseband-tx/dsp_fir_taps.hpp @@ -0,0 +1,205 @@ +/* + * 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_FIR_TAPS_H__ +#define __DSP_FIR_TAPS_H__ + +#include +#include + +#include "complex.hpp" + +template +struct fir_taps_real { + float pass_frequency_normalized; + float stop_frequency_normalized; + std::array taps; +}; + +/* 3kHz/6.7kHz @ 96kHz. sum(abs(taps)): 89429 */ +constexpr fir_taps_real<64> taps_64_lp_031_070_tfilter { + .pass_frequency_normalized = 0.031f, + .stop_frequency_normalized = 0.070f, + .taps = { { + 56, 58, 81, 100, 113, 112, 92, 49, + -21, -120, -244, -389, -543, -692, -819, -903, + -923, -861, -698, -424, -34, 469, 1073, 1756, + 2492, 3243, 3972, 4639, 5204, 5634, 5903, 5995, + 5903, 5634, 5204, 4639, 3972, 3243, 2492, 1756, + 1073, 469, -34, -424, -698, -861, -923, -903, + -819, -692, -543, -389, -244, -120, -21, 49, + 92, 112, 113, 100, 81, 58, 56, 0, + } }, +}; + +/* 4kHz/7.5kHz @ 96kHz. sum(abs(taps)): 96783 */ +constexpr fir_taps_real<64> taps_64_lp_042_078_tfilter { + .pass_frequency_normalized = 0.042f, + .stop_frequency_normalized = 0.078f, + .taps = { { + -19, 39, 72, 126, 197, 278, 360, 432, + 478, 485, 438, 327, 152, -82, -359, -651, + -922, -1132, -1236, -1192, -968, -545, 81, 892, + 1852, 2906, 3984, 5012, 5910, 6609, 7053, 7205, + 7053, 6609, 5910, 5012, 3984, 2906, 1852, 892, + 81, -545, -968, -1192, -1236, -1132, -922, -651, + -359, -82, 152, 327, 438, 485, 478, 432, + 360, 278, 197, 126, 72, 39, -19, 0, + } }, +}; + +/* 5kHz/8.5kHz @ 96kHz. sum(abs(taps)): 101312 */ +constexpr fir_taps_real<64> taps_64_lp_052_089_tfilter { + .pass_frequency_normalized = 0.052f, + .stop_frequency_normalized = 0.089f, + .taps = { { + -65, -88, -129, -163, -178, -160, -100, 9, + 160, 340, 523, 675, 758, 738, 591, 313, + -76, -533, -987, -1355, -1544, -1472, -1077, -335, + 738, 2078, 3579, 5104, 6502, 7627, 8355, 8608, + 8355, 7627, 6502, 5104, 3579, 2078, 738, -335, + -1077, -1472, -1544, -1355, -987, -533, -76, 313, + 591, 738, 758, 675, 523, 340, 160, 9, + -100, -160, -178, -163, -129, -88, -65, 0, + } }, +}; + +/* 6kHz/9.6kHz @ 96kHz. sum(abs(taps)): 105088 */ +constexpr fir_taps_real<64> taps_64_lp_063_100_tfilter { + .pass_frequency_normalized = 0.063f, + .stop_frequency_normalized = 0.100f, + .taps = { { + 43, 21, -2, -54, -138, -245, -360, -453, + -493, -451, -309, -73, 227, 535, 776, 876, + 773, 443, -86, -730, -1357, -1801, -1898, -1515, + -585, 869, 2729, 4794, 6805, 8490, 9611, 10004, + 9611, 8490, 6805, 4794, 2729, 869, -585, -1515, + -1898, -1801, -1357, -730, -86, 443, 773, 876, + 776, 535, 227, -73, -309, -451, -493, -453, + -360, -245, -138, -54, -2, 21, 43, 0, + } }, +}; + +/* 7kHz/10.4kHz @ 96kHz: sum(abs(taps)): 110157 */ +constexpr fir_taps_real<64> taps_64_lp_073_108_tfilter { + .pass_frequency_normalized = 0.073f, + .stop_frequency_normalized = 0.108f, + .taps = { { + 79, 145, 241, 334, 396, 394, 306, 130, + -109, -360, -550, -611, -494, -197, 229, 677, + 1011, 1096, 846, 257, -570, -1436, -2078, -2225, + -1670, -327, 1726, 4245, 6861, 9146, 10704, 11257, + 10704, 9146, 6861, 4245, 1726, -327, -1670, -2225, + -2078, -1436, -570, 257, 846, 1096, 1011, 677, + 229, -197, -494, -611, -550, -360, -109, 130, + 306, 394, 396, 334, 241, 145, 79, 0, + } }, +}; + +/* 8kHz/11.5kHz @ 96kHz. sum(abs(taps)): 112092 */ +constexpr fir_taps_real<64> taps_64_lp_083_120_tfilter { + .pass_frequency_normalized = 0.083f, + .stop_frequency_normalized = 0.120f, + .taps = { { + -63, -72, -71, -21, 89, 248, 417, 537, + 548, 407, 124, -237, -563, -723, -621, -238, + 337, 919, 1274, 1201, 617, -382, -1514, -2364, + -2499, -1600, 414, 3328, 6651, 9727, 11899, 12682, + 11899, 9727, 6651, 3328, 414, -1600, -2499, -2364, + -1514, -382, 617, 1201, 1274, 919, 337, -238, + -621, -723, -563, -237, 124, 407, 548, 537, + 417, 248, 89, -21, -71, -72, -63, 0, + } }, +}; + +/* 9kHz/12.4kHz @ 96kHz. sum(abs(taps)): 116249 */ +constexpr fir_taps_real<64> taps_64_lp_094_129_tfilter { + .pass_frequency_normalized = 0.094f, + .stop_frequency_normalized = 0.129f, + .taps = { { + 5, -93, -198, -335, -449, -478, -378, -144, + 166, 444, 563, 440, 82, -395, -788, -892, + -589, 73, 859, 1421, 1431, 734, -530, -1919, + -2798, -2555, -837, 2274, 6220, 10103, 12941, 13981, + 12941, 10103, 6220, 2274, -837, -2555, -2798, -1919, + -530, 734, 1431, 1421, 859, 73, -589, -892, + -788, -395, 82, 440, 563, 444, 166, -144, + -378, -478, -449, -335, -198, -93, 5, 0, + } }, +}; + +/* 10kHz/13.4kHz @ 96kHz. sum(abs(taps)): 118511 */ +constexpr fir_taps_real<64> taps_64_lp_104_140_tfilter { + .pass_frequency_normalized = 0.104f, + .stop_frequency_normalized = 0.140f, + .taps = { { + 89, 159, 220, 208, 84, -147, -412, -597, + -588, -345, 58, 441, 595, 391, -128, -730, + -1080, -914, -198, 793, 1558, 1594, 678, -942, + -2546, -3187, -2084, 992, 5515, 10321, 13985, 15353, + 13985, 10321, 5515, 992, -2084, -3187, -2546, -942, + 678, 1594, 1558, 793, -198, -914, -1080, -730, + -128, 391, 595, 441, 58, -345, -588, -597, + -412, -147, 84, 208, 220, 159, 89, 0, + } }, +}; + +/* Wideband FM channel filter + * 103kHz/128kHz @ 768kHz + */ +constexpr fir_taps_real<64> taps_64_lp_130_169_tfilter { + .pass_frequency_normalized = 0.130f, + .stop_frequency_normalized = 0.169f, + .taps = { { + 100, 127, 62, -157, -470, -707, -678, -332, + 165, 494, 400, -85, -610, -729, -253, 535, + 1026, 734, -263, -1264, -1398, -332, 1316, 2259, + 1447, -988, -3474, -3769, -385, 6230, 13607, 18450, + 18450, 13607, 6230, -385, -3769, -3474, -988, 1447, + 2259, 1316, -332, -1398, -1264, -263, 734, 1026, + 535, -253, -729, -610, -85, 400, 494, 165, + -332, -678, -707, -470, -157, 62, 127, 100, + } }, +}; + +/* Wideband audio filter */ +/* 96kHz int16_t input + * -> FIR filter, <15kHz (0.156fs) pass, >19kHz (0.198fs) stop + * -> 48kHz int16_t output, gain of 1.0 (I think). + * Padded to multiple of four taps for unrolled FIR code. + * sum(abs(taps)): 125270 + */ +constexpr fir_taps_real<64> taps_64_lp_156_198 { + .pass_frequency_normalized = 0.156f, + .stop_frequency_normalized = 0.196f, + .taps = { { + -27, 166, 104, -36, -174, -129, 109, 287, + 148, -232, -430, -130, 427, 597, 49, -716, + -778, 137, 1131, 957, -493, -1740, -1121, 1167, + 2733, 1252, -2633, -4899, -1336, 8210, 18660, 23254, + 18660, 8210, -1336, -4899, -2633, 1252, 2733, 1167, + -1121, -1740, -493, 957, 1131, 137, -778, -716, + 49, 597, 427, -130, -430, -232, 148, 287, + 109, -129, -174, -36, 104, 166, -27, 0, + } }, +}; + +#endif/*__DSP_FIR_TAPS_H__*/ diff --git a/firmware/baseband-tx/dsp_iir.hpp b/firmware/baseband-tx/dsp_iir.hpp new file mode 100644 index 00000000..8fb12f05 --- /dev/null +++ b/firmware/baseband-tx/dsp_iir.hpp @@ -0,0 +1,75 @@ +/* + * 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_IIR_H__ +#define __DSP_IIR_H__ + +#include + +#include "dsp_types.hpp" + +struct iir_biquad_config_t { + const std::array b; + const std::array a; +}; + +class IIRBiquadFilter { +public: + // http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt + + // Assume all coefficients are normalized so that a0=1.0 + constexpr IIRBiquadFilter( + const iir_biquad_config_t& config + ) : config(config) + { + } + + void execute(buffer_s16_t buffer_in, buffer_s16_t buffer_out) { + // TODO: Assert that buffer_out.count == buffer_in.count. + for(size_t i=0; i x { { 0.0f, 0.0f, 0.0f } }; + std::array y { { 0.0f, 0.0f, 0.0f } }; + + float execute_sample(const float in) { + x[0] = x[1]; + x[1] = x[2]; + x[2] = in; + + y[0] = y[1]; + y[1] = y[2]; + y[2] = config.b[0] * x[2] + config.b[1] * x[1] + config.b[2] * x[0] + - config.a[1] * y[1] - config.a[2] * y[0]; + + return y[2]; + } +}; + +#endif/*__DSP_IIR_H__*/ diff --git a/firmware/baseband-tx/dsp_iir_config.hpp b/firmware/baseband-tx/dsp_iir_config.hpp new file mode 100644 index 00000000..e20a51fc --- /dev/null +++ b/firmware/baseband-tx/dsp_iir_config.hpp @@ -0,0 +1,37 @@ +/* + * 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_IIR_CONFIG_H__ +#define __DSP_IIR_CONFIG_H__ + +#include "dsp_iir.hpp" + +constexpr iir_biquad_config_t audio_hpf_config { + { 0.93346032f, -1.86687724f, 0.93346032f }, + { 1.0f , -1.97730264f, 0.97773668f } +}; + +constexpr iir_biquad_config_t non_audio_hpf_config { + { 0.51891061f, -0.95714180f, 0.51891061f }, + { 1.0f , -0.79878302f, 0.43960231f } +}; + +#endif/*__DSP_IIR_CONFIG_H__*/ diff --git a/firmware/baseband-tx/dsp_squelch.cpp b/firmware/baseband-tx/dsp_squelch.cpp new file mode 100644 index 00000000..4cfe651a --- /dev/null +++ b/firmware/baseband-tx/dsp_squelch.cpp @@ -0,0 +1,45 @@ +/* + * 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 +#include + +bool FMSquelch::execute(buffer_s16_t audio) { + // TODO: No hard-coded array size. + std::array squelch_energy_buffer; + const buffer_s16_t squelch_energy { + squelch_energy_buffer.data(), + squelch_energy_buffer.size() + }; + non_audio_hpf.execute(audio, squelch_energy); + + uint64_t max_squared = 0; + for(const auto sample : squelch_energy_buffer) { + const uint64_t sample_squared = sample * sample; + if( sample_squared > max_squared ) { + max_squared = sample_squared; + } + } + + return (max_squared < (threshold * threshold)); +} diff --git a/firmware/baseband-tx/dsp_squelch.hpp b/firmware/baseband-tx/dsp_squelch.hpp new file mode 100644 index 00000000..06cb2cdd --- /dev/null +++ b/firmware/baseband-tx/dsp_squelch.hpp @@ -0,0 +1,45 @@ +/* + * 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 +#include + +class FMSquelch { +public: + bool execute(buffer_s16_t audio); + +private: + static constexpr size_t N = 32; + static constexpr int16_t threshold = 3072; + + // nyquist = 48000 / 2.0 + // scipy.signal.iirdesign(wp=8000 / nyquist, ws= 4000 / nyquist, gpass=1, gstop=18, ftype='ellip') + IIRBiquadFilter non_audio_hpf { non_audio_hpf_config }; +}; + +#endif/*__DSP_SQUELCH_H__*/ diff --git a/firmware/baseband-tx/event_m4.cpp b/firmware/baseband-tx/event_m4.cpp new file mode 100644 index 00000000..50b0c0b7 --- /dev/null +++ b/firmware/baseband-tx/event_m4.cpp @@ -0,0 +1,30 @@ +/* + * 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 "ch.h" + +Thread* thread_event_loop = nullptr; + +void events_initialize(Thread* const event_loop_thread) { + thread_event_loop = event_loop_thread; +} diff --git a/firmware/baseband-tx/event_m4.hpp b/firmware/baseband-tx/event_m4.hpp new file mode 100644 index 00000000..be113ca8 --- /dev/null +++ b/firmware/baseband-tx/event_m4.hpp @@ -0,0 +1,46 @@ +/* + * 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 "ch.h" + +constexpr auto EVT_MASK_BASEBAND = EVENT_MASK(0); +constexpr auto EVT_MASK_SPECTRUM = EVENT_MASK(1); + +void events_initialize(Thread* const event_loop_thread); + +extern Thread* thread_event_loop; + +inline void events_flag(const eventmask_t events) { + if( thread_event_loop ) { + chEvtSignal(thread_event_loop, events); + } +} + +inline void events_flag_isr(const eventmask_t events) { + if( thread_event_loop ) { + chEvtSignalI(thread_event_loop, events); + } +} + +#endif/*__EVENT_M4_H__*/ diff --git a/firmware/baseband-tx/fxpt_atan2.cpp b/firmware/baseband-tx/fxpt_atan2.cpp new file mode 100644 index 00000000..a3531f79 --- /dev/null +++ b/firmware/baseband-tx/fxpt_atan2.cpp @@ -0,0 +1,152 @@ +/* + * 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 +#include +#include + +/** + * 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(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; + } + } +} diff --git a/firmware/baseband-tx/fxpt_atan2.hpp b/firmware/baseband-tx/fxpt_atan2.hpp new file mode 100644 index 00000000..b57d14e2 --- /dev/null +++ b/firmware/baseband-tx/fxpt_atan2.hpp @@ -0,0 +1,29 @@ +/* + * 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 + +int16_t fxpt_atan2(const int16_t y, const int16_t x); + +#endif/*__FXPT_ATAN2_H__*/ diff --git a/firmware/baseband-tx/gpdma_lli.hpp b/firmware/baseband-tx/gpdma_lli.hpp new file mode 100644 index 00000000..3bde2371 --- /dev/null +++ b/firmware/baseband-tx/gpdma_lli.hpp @@ -0,0 +1,225 @@ +/* + * 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 +#include + +#include +#include + +#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; + using chain_p = std::unique_ptr; + + Chain(const ChannelConfig& cc) : + chain(std::make_unique(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(lli), + }; + } +}; + +} /* namespace lli */ +} /* namespace gpdma */ +} /* namespace lpc43xx */ diff --git a/firmware/baseband-tx/halconf.h b/firmware/baseband-tx/halconf.h new file mode 100755 index 00000000..658d5869 --- /dev/null +++ b/firmware/baseband-tx/halconf.h @@ -0,0 +1,313 @@ +/* + 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 TRUE +#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_ */ + +/** @} */ diff --git a/firmware/baseband-tx/irq_ipc_m4.cpp b/firmware/baseband-tx/irq_ipc_m4.cpp new file mode 100644 index 00000000..07a75335 --- /dev/null +++ b/firmware/baseband-tx/irq_ipc_m4.cpp @@ -0,0 +1,54 @@ +/* + * 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 "irq_ipc_m4.hpp" + +#include "ch.h" +#include "hal.h" + +#include "event_m4.hpp" + +#include "lpc43xx_cpp.hpp" +using namespace lpc43xx; + +void m0apptxevent_interrupt_enable() { + nvicEnableVector(M0CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M0APPTXEVENT_IRQ_PRIORITY)); +} + +void m0apptxevent_interrupt_disable() { + nvicDisableVector(M0CORE_IRQn); +} + +extern "C" { + +CH_IRQ_HANDLER(MAPP_IRQHandler) { + CH_IRQ_PROLOGUE(); + + chSysLockFromIsr(); + events_flag_isr(EVT_MASK_BASEBAND); + chSysUnlockFromIsr(); + + creg::m0apptxevent::clear(); + + CH_IRQ_EPILOGUE(); +} + +} diff --git a/firmware/baseband-tx/irq_ipc_m4.hpp b/firmware/baseband-tx/irq_ipc_m4.hpp new file mode 100644 index 00000000..61328685 --- /dev/null +++ b/firmware/baseband-tx/irq_ipc_m4.hpp @@ -0,0 +1,28 @@ +/* + * 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 __IRQ_IPC_M4_H__ +#define __IRQ_IPC_M4_H__ + +void m0apptxevent_interrupt_enable(); +void m0apptxevent_interrupt_disable(); + +#endif/*__IRQ_IPC_M4_H__*/ diff --git a/firmware/baseband-tx/linear_resampler.hpp b/firmware/baseband-tx/linear_resampler.hpp new file mode 100644 index 00000000..d889a2c2 --- /dev/null +++ b/firmware/baseband-tx/linear_resampler.hpp @@ -0,0 +1,69 @@ +/* + * 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 + 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__*/ diff --git a/firmware/baseband-tx/main.cpp b/firmware/baseband-tx/main.cpp new file mode 100755 index 00000000..c5c6de20 --- /dev/null +++ b/firmware/baseband-tx/main.cpp @@ -0,0 +1,561 @@ +/* + * 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 "ch.h" +#include "test.h" + +#include "lpc43xx_cpp.hpp" + +#include "portapack_shared_memory.hpp" +#include "portapack_dma.hpp" + +#include "gpdma.hpp" + +#include "baseband.hpp" +#include "baseband_dma.hpp" + +#include "event_m4.hpp" + +#include "irq_ipc_m4.hpp" + +#include "touch_dma.hpp" + +#include "dsp_decimate.hpp" +#include "dsp_demodulate.hpp" +#include "dsp_fft.hpp" +#include "dsp_fir_taps.hpp" +#include "dsp_iir.hpp" +#include "dsp_iir_config.hpp" +#include "dsp_squelch.hpp" + +#include "channel_decimator.hpp" +#include "baseband_processor.hpp" +#include "proc_fsk_lcr.hpp" +#include "proc_jammer.hpp" + +#include "clock_recovery.hpp" +#include "packet_builder.hpp" + +#include "message_queue.hpp" + +#include "utility.hpp" + +#include "debug.hpp" + +#include "audio.hpp" +#include "audio_dma.hpp" + +#include "gcc.hpp" + +#include +#include +#include +#include +#include +#include + +static baseband::Direction direction = baseband::Direction::Receive; + +class ThreadBase { +public: + constexpr ThreadBase( + const char* const name + ) : name { name } + { + } + + static msg_t fn(void* arg) { + auto obj = static_cast(arg); + chRegSetThreadName(obj->name); + obj->run(); + + return 0; + } + + virtual void run() = 0; + +private: + const char* const name; +}; + +class BasebandThread : public ThreadBase { +public: + BasebandThread( + ) : ThreadBase { "baseband" } + { + } + + Thread* start(const tprio_t priority) { + return chThdCreateStatic(wa, sizeof(wa), + priority, ThreadBase::fn, + this + ); + } + + Thread* thread_main { nullptr }; + BasebandProcessor* baseband_processor { nullptr }; + BasebandConfiguration baseband_configuration; + +private: + WORKING_AREA(wa, 2048); + + void run() override { + + while(true) { + if (direction == baseband::Direction::Transmit) { + const auto buffer_tmp = baseband::dma::wait_for_tx_buffer(); + + const buffer_c8_t buffer { + buffer_tmp.p, buffer_tmp.count, baseband_configuration.sampling_rate + }; + + if( baseband_processor ) { + baseband_processor->execute(buffer); + } + } else { + const auto buffer_tmp = baseband::dma::wait_for_rx_buffer(); + + const buffer_c8_t buffer { + buffer_tmp.p, buffer_tmp.count, baseband_configuration.sampling_rate + }; + + if( baseband_processor ) { + baseband_processor->execute(buffer); + } + } + } + } +}; + +#define SAMPLES_PER_BIT 192 +#define FILTER_SIZE 576 +#define SAMPLE_BUFFER_SIZE SAMPLES_PER_BIT + FILTER_SIZE + +static int32_t waveform_biphase[] = { + 165,167,168,168,167,166,163,160, + 157,152,147,141,134,126,118,109, + 99,88,77,66,53,41,27,14, + 0,-14,-29,-44,-59,-74,-89,-105, + -120,-135,-150,-165,-179,-193,-206,-218, + -231,-242,-252,-262,-271,-279,-286,-291, + -296,-299,-301,-302,-302,-300,-297,-292, + -286,-278,-269,-259,-247,-233,-219,-202, + -185,-166,-145,-124,-101,-77,-52,-26, + 0,27,56,85,114,144,175,205, + 236,266,296,326,356,384,412,439, + 465,490,513,535,555,574,590,604, + 616,626,633,637,639,638,633,626, + 616,602,586,565,542,515,485,451, + 414,373,329,282,232,178,121,62, + 0,-65,-132,-202,-274,-347,-423,-500, + -578,-656,-736,-815,-894,-973,-1051,-1128, + -1203,-1276,-1347,-1415,-1479,-1540,-1596,-1648, + -1695,-1736,-1771,-1799,-1820,-1833,-1838,-1835, + -1822,-1800,-1767,-1724,-1670,-1605,-1527,-1437, + -1334,-1217,-1087,-943,-785,-611,-423,-219, + 0,235,487,755,1040,1341,1659,1994, + 2346,2715,3101,3504,3923,4359,4811,5280, + 5764,6264,6780,7310,7856,8415,8987,9573, + 10172,10782,11404,12036,12678,13329,13989,14656, + 15330,16009,16694,17382,18074,18767,19461,20155, + 20848,21539,22226,22909,23586,24256,24918,25571, + 26214,26845,27464,28068,28658,29231,29787,30325, + 30842,31339,31814,32266,32694,33097,33473,33823, + 34144,34437,34699,34931,35131,35299,35434,35535, + 35602,35634,35630,35591,35515,35402,35252,35065, + 34841,34579,34279,33941,33566,33153,32702,32214, + 31689,31128,30530,29897,29228,28525,27788,27017, + 26214,25379,24513,23617,22693,21740,20761,19755, + 18725,17672,16597,15501,14385,13251,12101,10935, + 9755,8563,7360,6148,4927,3701,2470,1235, + 0,-1235,-2470,-3701,-4927,-6148,-7360,-8563, + -9755,-10935,-12101,-13251,-14385,-15501,-16597,-17672, + -18725,-19755,-20761,-21740,-22693,-23617,-24513,-25379, + -26214,-27017,-27788,-28525,-29228,-29897,-30530,-31128, + -31689,-32214,-32702,-33153,-33566,-33941,-34279,-34579, + -34841,-35065,-35252,-35402,-35515,-35591,-35630,-35634, + -35602,-35535,-35434,-35299,-35131,-34931,-34699,-34437, + -34144,-33823,-33473,-33097,-32694,-32266,-31814,-31339, + -30842,-30325,-29787,-29231,-28658,-28068,-27464,-26845, + -26214,-25571,-24918,-24256,-23586,-22909,-22226,-21539, + -20848,-20155,-19461,-18767,-18074,-17382,-16694,-16009, + -15330,-14656,-13989,-13329,-12678,-12036,-11404,-10782, + -10172,-9573,-8987,-8415,-7856,-7310,-6780,-6264, + -5764,-5280,-4811,-4359,-3923,-3504,-3101,-2715, + -2346,-1994,-1659,-1341,-1040,-755,-487,-235, + 0,219,423,611,785,943,1087,1217, + 1334,1437,1527,1605,1670,1724,1767,1800, + 1822,1835,1838,1833,1820,1799,1771,1736, + 1695,1648,1596,1540,1479,1415,1347,1276, + 1203,1128,1051,973,894,815,736,656, + 578,500,423,347,274,202,132,65, + 0,-62,-121,-178,-232,-282,-329,-373, + -414,-451,-485,-515,-542,-565,-586,-602, + -616,-626,-633,-638,-639,-637,-633,-626, + -616,-604,-590,-574,-555,-535,-513,-490, + -465,-439,-412,-384,-356,-326,-296,-266, + -236,-205,-175,-144,-114,-85,-56,-27, + 0,26,52,77,101,124,145,166, + 185,202,219,233,247,259,269,278, + 286,292,297,300,302,302,301,299, + 296,291,286,279,271,262,252,242, + 231,218,206,193,179,165,150,135, + 120,105,89,74,59,44,29,14, + 0,-14,-27,-41,-53,-66,-77,-88, + -99,-109,-118,-126,-134,-141,-147,-152, + -157,-160,-163,-166,-167,-168,-168,-167 +}; + +class RDSProcessor : public BasebandProcessor { +public: + void execute(buffer_c8_t buffer) override { + + for (size_t i = 0; i= 9) { + s = 0; + if(sample_count >= SAMPLES_PER_BIT) { + cur_bit = (shared_memory.rdsdata[(bit_pos / 26) & 15]>>(25-(bit_pos % 26))) & 1; + prev_output = cur_output; + cur_output = prev_output ^ cur_bit; + + int32_t *src = waveform_biphase; + int idx = in_sample_index; + + for(int j=0; j= SAMPLE_BUFFER_SIZE) idx = 0; + } + + in_sample_index += SAMPLES_PER_BIT; + if (in_sample_index >= SAMPLE_BUFFER_SIZE) in_sample_index -= SAMPLE_BUFFER_SIZE; + + bit_pos++; + sample_count = 0; + } + + sample = sample_buffer[out_sample_index]; + sample_buffer[out_sample_index] = 0; + out_sample_index++; + if (out_sample_index >= SAMPLE_BUFFER_SIZE) out_sample_index = 0; + + //AM @ 228k/4=57kHz + switch (mphase) { + case 0: + case 2: sample = 0; break; + case 1: break; + case 3: sample = -sample; break; + } + mphase++; + if (mphase >= 4) mphase = 0; + + sample_count++; + } else { + s++; + } + + //FM + frq = (sample>>16) * 386760; + + phase = (phase + frq); + sphase = phase + (256<<16); + + //re = sintab[(sphase & 0x03FF0000)>>16]; + //im = sintab[(phase & 0x03FF0000)>>16]; + + buffer.p[i] = {(int8_t)re,(int8_t)im}; + } + } + +private: + int8_t re, im; + uint8_t mphase, s; + uint32_t bit_pos; + int32_t sample_buffer[SAMPLE_BUFFER_SIZE] = {0}; + int32_t val; + uint8_t prev_output = 0; + uint8_t cur_output = 0; + uint8_t cur_bit = 0; + int sample_count = SAMPLES_PER_BIT; + int in_sample_index = 0; + int32_t sample; + int out_sample_index = SAMPLE_BUFFER_SIZE-1; + uint32_t phase, sphase; + int32_t sig, frq, frq_im, rdsc; + int32_t k; +}; + +class ToneProcessor : public BasebandProcessor { +public: + void execute(buffer_c8_t buffer) override { + + for (size_t i = 0; i= 9) { + s = 0; + aphase += 353205; // DEBUG + //sample = sintab[(aphase & 0x03FF0000)>>16]; + } else { + s++; + } + + //sample = sintab[(aphase & 0x03FF0000)>>16]; + + //FM + frq = sample * 500; // DEBUG + + phase = (phase + frq); + sphase = phase + (256<<16); + + //re = sintab[(sphase & 0x03FF0000)>>16]; + //im = sintab[(phase & 0x03FF0000)>>16]; + + buffer.p[i] = {(int8_t)re,(int8_t)im}; + } + } + +private: + int8_t re, im; + uint8_t s; + uint32_t sample_count; + uint32_t aphase, phase, sphase; + int32_t sample, sig, frq; +}; + +extern "C" { + +void __late_init(void) { + /* + * System initializations. + * - HAL initialization, this also initializes the configured device drivers + * and performs the board-specific initializations. + * - Kernel initialization, the main() function becomes a thread and the + * RTOS is active. + */ + halInit(); + + /* After this call, scheduler, systick, heap, etc. are available. */ + /* By doing chSysInit() here, it runs before C++ constructors, which may + * require the heap. + */ + chSysInit(); +} + +} + +static BasebandThread baseband_thread; + +static void init() { + i2s::i2s0::configure( + audio::i2s0_config_tx, + audio::i2s0_config_rx, + audio::i2s0_config_dma + ); + + audio::dma::init(); + audio::dma::configure(); + audio::dma::enable(); + + i2s::i2s0::tx_start(); + i2s::i2s0::rx_start(); + + LPC_CREG->DMAMUX = portapack::gpdma_mux; + gpdma::controller.enable(); + nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY)); + + baseband::dma::init(); + + touch::dma::init(); + + const auto thread_main = chThdSelf(); + + baseband_thread.thread_main = thread_main; + + baseband_thread.start(NORMALPRIO + 20); +} + +static void shutdown() { + // TODO: Is this complete? + + nvicDisableVector(DMA_IRQn); + + m0apptxevent_interrupt_disable(); + + chSysDisable(); + + systick_stop(); +} + +static void halt() { + port_disable(); + while(true) { + port_wait_for_interrupt(); + } +} + +class EventDispatcher { +public: + MessageHandlerMap& message_handlers() { + return message_map; + } + + void run() { + while(is_running) { + const auto events = wait(); + dispatch(events); + } + } + + void request_stop() { + is_running = false; + } + +private: + MessageHandlerMap message_map; + + bool is_running = true; + + eventmask_t wait() { + return chEvtWaitAny(ALL_EVENTS); + } + + void dispatch(const eventmask_t events) { + if( events & EVT_MASK_BASEBAND ) { + handle_baseband_queue(); + } + + if( events & EVT_MASK_SPECTRUM ) { + handle_spectrum(); + } + } + + void handle_baseband_queue() { + std::array message_buffer; + while(Message* const message = shared_memory.baseband_queue.pop(message_buffer)) { + message_map.send(message); + } + } + + void handle_spectrum() { + if( baseband_thread.baseband_processor ) { + baseband_thread.baseband_processor->update_spectrum(); + } + } +}; + +const auto baseband_buffer = + new std::array(); + +int main(void) { + init(); + + events_initialize(chThdSelf()); + m0apptxevent_interrupt_enable(); + + EventDispatcher event_dispatcher; + auto& message_handlers = event_dispatcher.message_handlers(); + + message_handlers.register_handler(Message::ID::BasebandConfiguration, + [&message_handlers](const Message* const p) { + auto message = reinterpret_cast(p); + if( message->configuration.mode != baseband_thread.baseband_configuration.mode ) { + + if( baseband_thread.baseband_processor ) { + i2s::i2s0::tx_mute(); + baseband::dma::disable(); + } + + // TODO: Timing problem around disabling DMA and nulling and deleting old processor + auto old_p = baseband_thread.baseband_processor; + baseband_thread.baseband_processor = nullptr; + delete old_p; + + switch(message->configuration.mode) { + case 15: + direction = baseband::Direction::Transmit; + baseband_thread.baseband_processor = new RDSProcessor(); + break; + + case 16: + direction = baseband::Direction::Transmit; + baseband_thread.baseband_processor = new LCRFSKProcessor(); + break; + + case 17: + direction = baseband::Direction::Transmit; + baseband_thread.baseband_processor = new ToneProcessor(); + break; + + case 18: + direction = baseband::Direction::Transmit; + baseband_thread.baseband_processor = new JammerProcessor(); + break; + + default: + break; + } + + if( baseband_thread.baseband_processor ) + baseband::dma::enable(direction); + } + + baseband::dma::configure( + baseband_buffer->data(), + direction + ); + + baseband_thread.baseband_configuration = message->configuration; + } + ); + + message_handlers.register_handler(Message::ID::Shutdown, + [&event_dispatcher](const Message* const) { + event_dispatcher.request_stop(); + } + ); + + /* TODO: Ensure DMAs are configured to point at first LLI in chain. */ + + touch::dma::allocate(); + touch::dma::enable(); + + baseband::dma::configure( + baseband_buffer->data(), + direction + ); + + //baseband::dma::allocate(4, 2048); + + event_dispatcher.run(); + + shutdown(); + + ShutdownMessage shutdown_message; + shared_memory.application_queue.push(shutdown_message); + + halt(); + + return 0; +} diff --git a/firmware/baseband-tx/main.cpp.orig b/firmware/baseband-tx/main.cpp.orig new file mode 100755 index 00000000..6dd555b5 --- /dev/null +++ b/firmware/baseband-tx/main.cpp.orig @@ -0,0 +1,1003 @@ +/* + * 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 "ch.h" +#include "test.h" + +#include "lpc43xx_cpp.hpp" + +#include "portapack_shared_memory.hpp" +#include "portapack_dma.hpp" +#include "gpdma.hpp" + +#include "baseband.hpp" +#include "baseband_dma.hpp" + +#include "event_m4.hpp" + +#include "irq_ipc_m4.hpp" + +#include "rssi.hpp" +#include "rssi_dma.hpp" + +#include "touch_dma.hpp" + +#include "dsp_decimate.hpp" +#include "dsp_demodulate.hpp" +#include "dsp_fir_taps.hpp" +#include "dsp_iir.hpp" +#include "dsp_iir_config.hpp" +#include "dsp_squelch.hpp" + +#include "baseband_stats_collector.hpp" +#include "rssi_stats_collector.hpp" + +#include "channel_decimator.hpp" +#include "baseband_processor.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 "clock_recovery.hpp" +#include "packet_builder.hpp" + +#include "message_queue.hpp" + +#include "utility.hpp" + +#include "debug.hpp" + +#include "audio.hpp" +#include "audio_dma.hpp" + +#include "gcc.hpp" + +#include +#include +#include +#include +#include +#include + +class ThreadBase { +public: + constexpr ThreadBase( + const char* const name + ) : name { name } + { + } + + static msg_t fn(void* arg) { + auto obj = static_cast(arg); + chRegSetThreadName(obj->name); + obj->run(); + + return 0; + } + + virtual void run() = 0; + +private: + const char* const name; +}; + +class BasebandThread : public ThreadBase { +public: + BasebandThread( + ) : ThreadBase { "baseband" } + { + } + + Thread* start(const tprio_t priority) { + return chThdCreateStatic(wa, sizeof(wa), + priority, ThreadBase::fn, + this + ); + } + +<<<<<<< HEAD + void fill_audio_buffer(const buffer_s16_t audio) { + auto audio_buffer = audio::dma::tx_empty_buffer(); + for(size_t i=0; i>>>>>> upstream/master + +private: + WORKING_AREA(wa, 2048); + + void run() override { + 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_rx_buffer(); + const 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); + } + ); + } + } +}; + +class RSSIThread : public ThreadBase { +public: + RSSIThread( + ) : ThreadBase { "rssi" } + { + } + + Thread* start(const tprio_t priority) { + return chThdCreateStatic(wa, sizeof(wa), + priority, ThreadBase::fn, + this + ); + } + + uint32_t sampling_rate { 400000 }; + +private: + WORKING_AREA(wa, 128); + + void run() override { + 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); + } + ); + } + } +}; + +<<<<<<< HEAD +static const int8_t sintab[1024] = { +0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 12, 13, 14, 15, 16, 16, 17, 18, 19, 19, 20, 21, 22, 22, 23, 24, 25, 26, 26, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 38, 39, 40, 41, 41, 42, 43, 44, 44, 45, 46, 46, 47, 48, 49, 49, 50, 51, 51, 52, 53, 54, 54, 55, 56, 56, 57, 58, 58, 59, 60, 61, 61, 62, 63, 63, 64, 65, 65, 66, 67, 67, 68, 69, 69, 70, 71, 71, 72, 72, 73, 74, 74, 75, 76, 76, 77, 78, 78, 79, 79, 80, 81, 81, 82, 82, 83, 84, 84, 85, 85, 86, 86, 87, 88, 88, 89, 89, 90, 90, 91, 91, 92, 93, 93, 94, 94, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 106, 107, 107, 108, 108, 109, 109, 109, 110, 110, 111, 111, 111, 112, 112, 112, 113, 113, 113, 114, 114, 114, 115, 115, 115, 116, 116, 116, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 120, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 122, 123, 123, 123, 123, 123, 124, 124, 124, 124, 124, 124, 125, 125, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, +127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 122, 122, 122, 122, 122, 121, 121, 121, 121, 120, 120, 120, 120, 119, 119, 119, 118, 118, 118, 118, 117, 117, 117, 116, 116, 116, 115, 115, 115, 114, 114, 114, 113, 113, 113, 112, 112, 112, 111, 111, 111, 110, 110, 109, 109, 109, 108, 108, 107, 107, 106, 106, 106, 105, 105, 104, 104, 103, 103, 102, 102, 102, 101, 101, 100, 100, 99, 99, 98, 98, 97, 97, 96, 96, 95, 95, 94, 94, 93, 93, 92, 91, 91, 90, 90, 89, 89, 88, 88, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81, 81, 80, 79, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 71, 70, 69, 69, 68, 67, 67, 66, 65, 65, 64, 63, 63, 62, 61, 61, 60, 59, 58, 58, 57, 56, 56, 55, 54, 54, 53, 52, 51, 51, 50, 49, 49, 48, 47, 46, 46, 45, 44, 44, 43, 42, 41, 41, 40, 39, 38, 38, 37, 36, 35, 35, 34, 33, 32, 32, 31, 30, 29, 29, 28, 27, 26, 26, 25, 24, 23, 22, 22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 14, 13, 12, 12, 11, 10, 9, 9, 8, 7, 6, 5, 5, 4, 3, 2, 2, 1, 0, -1, -2, -2, -3, -4, -5, -5, -6, -7, -8, -9, -9, -10, -11, -12, -12, -13, -14, -15, -16, -16, -17, -18, -19, -19, -20, -21, -22, -22, -23, -24, -25, -26, -26, -27, -28, -29, -29, -30, -31, +-32, -32, -33, -34, -35, -35, -36, -37, -38, -38, -39, -40, -41, -41, -42, -43, -44, -44, -45, -46, -46, -47, -48, -49, -49, -50, -51, -51, -52, -53, -54, -54, -55, -56, -56, -57, -58, -58, -59, -60, -61, -61, -62, -63, -63, -64, -65, -65, -66, -67, -67, -68, -69, -69, -70, -71, -71, -72, -72, -73, -74, -74, -75, -76, -76, -77, -78, -78, -79, -79, -80, -81, -81, -82, -82, -83, -84, -84, -85, -85, -86, -86, -87, -88, -88, -89, -89, -90, -90, -91, -91, -92, -93, -93, -94, -94, -95, -95, -96, -96, -97, -97, -98, -98, -99, -99, -100, -100, -101, -101, -102, -102, -102, -103, -103, -104, -104, -105, -105, -106, -106, -106, -107, -107, -108, -108, -109, -109, -109, -110, -110, -111, -111, -111, -112, -112, -112, -113, -113, -113, -114, -114, -114, -115, -115, -115, -116, -116, -116, -117, -117, -117, -118, -118, -118, -118, -119, -119, -119, -120, -120, -120, -120, -121, -121, -121, -121, -122, -122, -122, -122, -122, -123, -123, -123, -123, -123, -124, -124, -124, -124, -124, -124, -125, -125, -125, -125, -125, -125, -125, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, +-126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -125, -125, -125, -125, -125, -125, -125, -124, -124, -124, -124, -124, -124, -123, -123, -123, -123, -123, -122, -122, -122, -122, -122, -121, -121, -121, -121, -120, -120, -120, -120, -119, -119, -119, -118, -118, -118, -118, -117, -117, -117, -116, -116, -116, -115, -115, -115, -114, -114, -114, -113, -113, -113, -112, -112, -112, -111, -111, -111, -110, -110, -109, -109, -109, -108, -108, -107, -107, -106, -106, -106, -105, -105, -104, -104, -103, -103, -102, -102, -102, -101, -101, -100, -100, -99, -99, -98, -98, -97, -97, -96, -96, -95, -95, -94, -94, -93, -93, -92, -91, -91, -90, -90, -89, -89, -88, -88, -87, -86, -86, -85, -85, -84, -84, -83, -82, -82, -81, -81, -80, -79, -79, -78, -78, -77, -76, -76, -75, -74, -74, -73, -72, -72, -71, -71, -70, -69, -69, -68, -67, -67, -66, -65, -65, -64, -63, -63, -62, -61, -61, -60, -59, -58, -58, -57, -56, -56, -55, -54, -54, -53, -52, -51, -51, -50, -49, -49, -48, -47, -46, -46, -45, -44, -44, -43, -42, -41, -41, -40, -39, -38, -38, -37, -36, -35, -35, -34, -33, -32, -32, -31, -30, -29, -29, -28, -27, -26, -26, -25, -24, -23, -22, -22, -21, -20, -19, -19, -18, -17, -16, -16, -15, -14, -13, -12, -12, -11, -10, -9, -9, -8, -7, -6, -5, -5, -4, -3, -2, -2, -1 +}; + +#define SAMPLES_PER_BIT 192 +#define FILTER_SIZE 576 +#define SAMPLE_BUFFER_SIZE SAMPLES_PER_BIT + FILTER_SIZE + +static int32_t waveform_biphase[] = { + 165,167,168,168,167,166,163,160, + 157,152,147,141,134,126,118,109, + 99,88,77,66,53,41,27,14, + 0,-14,-29,-44,-59,-74,-89,-105, + -120,-135,-150,-165,-179,-193,-206,-218, + -231,-242,-252,-262,-271,-279,-286,-291, + -296,-299,-301,-302,-302,-300,-297,-292, + -286,-278,-269,-259,-247,-233,-219,-202, + -185,-166,-145,-124,-101,-77,-52,-26, + 0,27,56,85,114,144,175,205, + 236,266,296,326,356,384,412,439, + 465,490,513,535,555,574,590,604, + 616,626,633,637,639,638,633,626, + 616,602,586,565,542,515,485,451, + 414,373,329,282,232,178,121,62, + 0,-65,-132,-202,-274,-347,-423,-500, + -578,-656,-736,-815,-894,-973,-1051,-1128, + -1203,-1276,-1347,-1415,-1479,-1540,-1596,-1648, + -1695,-1736,-1771,-1799,-1820,-1833,-1838,-1835, + -1822,-1800,-1767,-1724,-1670,-1605,-1527,-1437, + -1334,-1217,-1087,-943,-785,-611,-423,-219, + 0,235,487,755,1040,1341,1659,1994, + 2346,2715,3101,3504,3923,4359,4811,5280, + 5764,6264,6780,7310,7856,8415,8987,9573, + 10172,10782,11404,12036,12678,13329,13989,14656, + 15330,16009,16694,17382,18074,18767,19461,20155, + 20848,21539,22226,22909,23586,24256,24918,25571, + 26214,26845,27464,28068,28658,29231,29787,30325, + 30842,31339,31814,32266,32694,33097,33473,33823, + 34144,34437,34699,34931,35131,35299,35434,35535, + 35602,35634,35630,35591,35515,35402,35252,35065, + 34841,34579,34279,33941,33566,33153,32702,32214, + 31689,31128,30530,29897,29228,28525,27788,27017, + 26214,25379,24513,23617,22693,21740,20761,19755, + 18725,17672,16597,15501,14385,13251,12101,10935, + 9755,8563,7360,6148,4927,3701,2470,1235, + 0,-1235,-2470,-3701,-4927,-6148,-7360,-8563, + -9755,-10935,-12101,-13251,-14385,-15501,-16597,-17672, + -18725,-19755,-20761,-21740,-22693,-23617,-24513,-25379, + -26214,-27017,-27788,-28525,-29228,-29897,-30530,-31128, + -31689,-32214,-32702,-33153,-33566,-33941,-34279,-34579, + -34841,-35065,-35252,-35402,-35515,-35591,-35630,-35634, + -35602,-35535,-35434,-35299,-35131,-34931,-34699,-34437, + -34144,-33823,-33473,-33097,-32694,-32266,-31814,-31339, + -30842,-30325,-29787,-29231,-28658,-28068,-27464,-26845, + -26214,-25571,-24918,-24256,-23586,-22909,-22226,-21539, + -20848,-20155,-19461,-18767,-18074,-17382,-16694,-16009, + -15330,-14656,-13989,-13329,-12678,-12036,-11404,-10782, + -10172,-9573,-8987,-8415,-7856,-7310,-6780,-6264, + -5764,-5280,-4811,-4359,-3923,-3504,-3101,-2715, + -2346,-1994,-1659,-1341,-1040,-755,-487,-235, + 0,219,423,611,785,943,1087,1217, + 1334,1437,1527,1605,1670,1724,1767,1800, + 1822,1835,1838,1833,1820,1799,1771,1736, + 1695,1648,1596,1540,1479,1415,1347,1276, + 1203,1128,1051,973,894,815,736,656, + 578,500,423,347,274,202,132,65, + 0,-62,-121,-178,-232,-282,-329,-373, + -414,-451,-485,-515,-542,-565,-586,-602, + -616,-626,-633,-638,-639,-637,-633,-626, + -616,-604,-590,-574,-555,-535,-513,-490, + -465,-439,-412,-384,-356,-326,-296,-266, + -236,-205,-175,-144,-114,-85,-56,-27, + 0,26,52,77,101,124,145,166, + 185,202,219,233,247,259,269,278, + 286,292,297,300,302,302,301,299, + 296,291,286,279,271,262,252,242, + 231,218,206,193,179,165,150,135, + 120,105,89,74,59,44,29,14, + 0,-14,-27,-41,-53,-66,-77,-88, + -99,-109,-118,-126,-134,-141,-147,-152, + -157,-160,-163,-166,-167,-168,-168,-167 +}; + +/* +class RDSProcessor : public BasebandProcessor { +public: + void execute(buffer_c8_t buffer) override { + + for (size_t i = 0; i= 9) { + s = 0; + if(sample_count >= SAMPLES_PER_BIT) { + cur_bit = (shared_memory.rdsdata[(bit_pos / 26) & 15]>>(25-(bit_pos % 26))) & 1; + prev_output = cur_output; + cur_output = prev_output ^ cur_bit; + + int32_t *src = waveform_biphase; + int idx = in_sample_index; + + for(int j=0; j= SAMPLE_BUFFER_SIZE) idx = 0; + } + + in_sample_index += SAMPLES_PER_BIT; + if (in_sample_index >= SAMPLE_BUFFER_SIZE) in_sample_index -= SAMPLE_BUFFER_SIZE; + + bit_pos++; + sample_count = 0; + } + + sample = sample_buffer[out_sample_index]; + sample_buffer[out_sample_index] = 0; + out_sample_index++; + if (out_sample_index >= SAMPLE_BUFFER_SIZE) out_sample_index = 0; + + //AM @ 228k/4=57kHz + switch (mphase) { + case 0: + case 2: sample = 0; break; + case 1: break; + case 3: sample = -sample; break; + } + mphase++; + if (mphase >= 4) mphase = 0; + + sample_count++; + } else { + s++; + } + + //FM + frq = (sample>>16) * 386760; + + phase = (phase + frq); + sphase = phase + (256<<16); + + re = sintab[(sphase & 0x03FF0000)>>16]; + im = sintab[(phase & 0x03FF0000)>>16]; + + buffer.p[i] = {(int8_t)re,(int8_t)im}; + } + } + +private: + int8_t re, im; + uint8_t mphase, s; + uint32_t bit_pos; + int32_t sample_buffer[SAMPLE_BUFFER_SIZE] = {0}; + int32_t val; + uint8_t prev_output = 0; + uint8_t cur_output = 0; + uint8_t cur_bit = 0; + int sample_count = SAMPLES_PER_BIT; + int in_sample_index = 0; + int32_t sample; + int out_sample_index = SAMPLE_BUFFER_SIZE-1; + uint32_t phase, sphase; + int32_t sig, frq, frq_im, rdsc; + int32_t k; +};*/ + +class LCRFSKProcessor : public BasebandProcessor { +public: + void execute(buffer_c8_t buffer) override { + + for (size_t i = 0; i= 9) { + s = 0; + + if (sample_count >= shared_memory.afsk_samples_per_bit) { + if (shared_memory.afsk_transmit_done == false) + cur_byte = shared_memory.lcrdata[byte_pos]; + if (!cur_byte) { + if (shared_memory.afsk_repeat) { + shared_memory.afsk_repeat--; + bit_pos = 0; + byte_pos = 0; + cur_byte = shared_memory.lcrdata[0]; + if( message.is_free() ) { + message.n = shared_memory.afsk_repeat; + shared_memory.application_queue.push(&message); + } + } else { + if( message.is_free() ) { + message.n = 0; + shared_memory.afsk_transmit_done = true; + shared_memory.application_queue.push(&message); + } + cur_byte = 0; + } + } + + gbyte = 0; + gbyte = cur_byte << 1; + gbyte |= 1; + + cur_bit = (gbyte >> (9-bit_pos)) & 1; + + if (bit_pos == 9) { + bit_pos = 0; + byte_pos++; + } else { + bit_pos++; + } + + //aphase = 0x2FFFFFF; + + sample_count = 0; + } else { + sample_count++; + } + if (cur_bit) + aphase += shared_memory.afsk_phase_inc_mark; //(353205) + else + aphase += shared_memory.afsk_phase_inc_space; //(647542) + + sample = sintab[(aphase & 0x03FF0000)>>16]; + } else { + s++; + } + + sample = sintab[(aphase & 0x03FF0000)>>16]; + + //FM + frq = sample * shared_memory.afsk_fmmod; + + phase = (phase + frq); + sphase = phase + (256<<16); + + re = sintab[(sphase & 0x03FF0000)>>16]; + im = sintab[(phase & 0x03FF0000)>>16]; + + buffer.p[i] = {(int8_t)re,(int8_t)im}; + } + } + +private: + int8_t re, im; + uint8_t s; + uint8_t bit_pos, byte_pos = 0; + char cur_byte = 0; + uint16_t gbyte; + uint8_t cur_bit = 0; + uint32_t sample_count; + uint32_t aphase, phase, sphase; + int32_t sample, sig, frq; + TXDoneMessage message; +}; + +/*class ToneProcessor : public BasebandProcessor { +public: + void execute(buffer_c8_t buffer) override { + + for (size_t i = 0; i= 9) { + s = 0; + aphase += 353205; // DEBUG + sample = sintab[(aphase & 0x03FF0000)>>16]; + } else { + s++; + } + + sample = sintab[(aphase & 0x03FF0000)>>16]; + + //FM + frq = sample * 500; // DEBUG + + phase = (phase + frq); + sphase = phase + (256<<16); + + re = sintab[(sphase & 0x03FF0000)>>16]; + im = sintab[(phase & 0x03FF0000)>>16]; + + buffer.p[i] = {(int8_t)re,(int8_t)im}; + } + } + +private: + int8_t re, im; + uint8_t s; + uint32_t sample_count; + uint32_t aphase, phase, sphase; + int32_t sample, sig, frq; +};*/ + +#define POLY_MASK_32 0xB4BCD35C + +class JammerProcessor : public BasebandProcessor { +public: + void execute(buffer_c8_t buffer) override { + + for (size_t i = 0; i 3000000) { + s = 0; + feedback = lfsr & 1; + lfsr >>= 1; + if (feedback == 1) + lfsr ^= POLY_MASK_32; + } else { + s++; + } + + aphase += lfsr;*/ + + /*if (s >= 10) { + s = 0; + aphase += 353205; // DEBUG + } else { + s++; + } + + sample = sintab[(aphase & 0x03FF0000)>>16];*/ + + // Duration timer + // + if (s >= 10000) { //shared_memory.jammer_ranges[ir].duration + s = 0; + for (;;) { + ir++; + if (ir > 15) ir = 0; + if (shared_memory.jammer_ranges[ir].active == true) break; + } + jammer_bw = shared_memory.jammer_ranges[ir].width; + + if( message.is_free() ) { + message.freq = shared_memory.jammer_ranges[ir].center; + shared_memory.application_queue.push(&message); + } + } else { + s++; + } + + // Ramp + /*if (r >= 10) { + if (sample < 128) + sample++; + else + sample = -127; + r = 0; + } else { + r++; + }*/ + + // Phase + if (r >= 70) { + aphase += ((aphase>>4) ^ 0x4573) << 14; + r = 0; + } else { + r++; + } + + aphase += 35320; + sample = sintab[(aphase & 0x03FF0000)>>16]; + + //FM + frq = sample * jammer_bw; // Bandwidth + + //65536 -> 0.6M + //131072 -> 1.2M + + phase = (phase + frq); + sphase = phase + (256<<16); + + re = sintab[(sphase & 0x03FF0000)>>16]; + im = sintab[(phase & 0x03FF0000)>>16]; + + buffer.p[i] = {(int8_t)re,(int8_t)im}; + } + } + +private: + int32_t lfsr32 = 0xABCDE; + uint32_t s; + int8_t r, ir, re, im; + int64_t jammer_bw, jammer_center; + int feedback; + int32_t lfsr; + uint32_t sample_count; + uint32_t aphase, phase, sphase; + int32_t sample, frq; + RetuneMessage message; +}; + +static BasebandProcessor* baseband_processor { nullptr }; +static BasebandConfiguration baseband_configuration; + +static baseband::Direction direction = baseband::Direction::Transmit; + +static WORKING_AREA(baseband_thread_wa, 8192); + +static __attribute__((noreturn)) msg_t baseband_fn(void *arg) { + (void)arg; + chRegSetThreadName("baseband"); + + BasebandStatsCollector stats; + BasebandStatisticsMessage message; + + while(true) { + if (direction == baseband::Direction::Transmit) { + const auto buffer_tmp = baseband::dma::wait_for_tx_buffer(); + + const buffer_c8_t buffer { + buffer_tmp.p, buffer_tmp.count, baseband_configuration.sampling_rate + }; + + if( baseband_processor ) { + baseband_processor->execute(buffer); + } + + stats.process(buffer, + [&message](const BasebandStatistics statistics) { + if( message.is_free() ) { + message.statistics = statistics; + shared_memory.application_queue.push(&message); + } + } + ); + } else { + const auto buffer_tmp = baseband::dma::wait_for_rx_buffer(); + + const buffer_c8_t buffer { + buffer_tmp.p, buffer_tmp.count, baseband_configuration.sampling_rate + }; + + if( baseband_processor ) { + baseband_processor->execute(buffer); + } + + stats.process(buffer, + [&message](const BasebandStatistics statistics) { + if( message.is_free() ) { + message.statistics = statistics; + shared_memory.application_queue.push(&message); + } + } + ); + } + } +} + +static WORKING_AREA(rssi_thread_wa, 128); +static __attribute__((noreturn)) msg_t rssi_fn(void *arg) { + (void)arg; + chRegSetThreadName("rssi"); + + RSSIStatisticsCollector stats; + RSSIStatisticsMessage message; + + 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, 400000 + }; + + stats.process( + buffer, + [&message](const RSSIStatistics statistics) { + if( message.is_free() ) { + message.statistics = statistics; + shared_memory.application_queue.push(&message); + } + } + ); + } +} + +======= +>>>>>>> upstream/master +extern "C" { + +void __late_init(void) { + /* + * System initializations. + * - HAL initialization, this also initializes the configured device drivers + * and performs the board-specific initializations. + * - Kernel initialization, the main() function becomes a thread and the + * RTOS is active. + */ + halInit(); + + /* After this call, scheduler, systick, heap, etc. are available. */ + /* By doing chSysInit() here, it runs before C++ constructors, which may + * require the heap. + */ + chSysInit(); +} + +} + +static BasebandThread baseband_thread; +static RSSIThread rssi_thread; + +static void init() { + i2s::i2s0::configure( + audio::i2s0_config_tx, + audio::i2s0_config_rx, + audio::i2s0_config_dma + ); + + audio::dma::init(); + audio::dma::configure(); + audio::dma::enable(); + + i2s::i2s0::tx_start(); + i2s::i2s0::rx_start(); + + LPC_CREG->DMAMUX = portapack::gpdma_mux; + gpdma::controller.enable(); + nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY)); + + baseband::dma::init(); + + rf::rssi::init(); + touch::dma::init(); + +<<<<<<< HEAD + chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa), + baseband_thread_priority, baseband_fn, + nullptr + ); + + chThdCreateStatic(rssi_thread_wa, sizeof(rssi_thread_wa), + rssi_thread_priority, rssi_fn, + nullptr + ); +======= + const auto thread_main = chThdSelf(); + + const auto thread_rssi = rssi_thread.start(NORMALPRIO + 10); + + baseband_thread.thread_main = thread_main; + baseband_thread.thread_rssi = thread_rssi; + + baseband_thread.start(NORMALPRIO + 20); +} + +static void shutdown() { + // TODO: Is this complete? + + nvicDisableVector(DMA_IRQn); + + m0apptxevent_interrupt_disable(); + + chSysDisable(); + + systick_stop(); +} + +static void halt() { + port_disable(); + while(true) { + port_wait_for_interrupt(); + } +>>>>>>> upstream/master +} + +class EventDispatcher { +public: + MessageHandlerMap& message_handlers() { + return message_map; + } + + void run() { + while(is_running) { + const auto events = wait(); + dispatch(events); + } + } + + void request_stop() { + is_running = false; + } + +private: + MessageHandlerMap message_map; + + bool is_running = true; + + eventmask_t wait() { + return chEvtWaitAny(ALL_EVENTS); + } + + void dispatch(const eventmask_t events) { + if( events & EVT_MASK_BASEBAND ) { + handle_baseband_queue(); + } + + if( events & EVT_MASK_SPECTRUM ) { + handle_spectrum(); + } + } + + void handle_baseband_queue() { + std::array message_buffer; + while(Message* const message = shared_memory.baseband_queue.pop(message_buffer)) { + message_map.send(message); + } + } + + void handle_spectrum() { + if( baseband_thread.baseband_processor ) { + baseband_thread.baseband_processor->update_spectrum(); + } + } +}; + +<<<<<<< HEAD +static void m0apptxevent_interrupt_enable() { + nvicEnableVector(M0CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M0APPTXEVENT_IRQ_PRIORITY)); +} + +extern "C" { + +CH_IRQ_HANDLER(MAPP_IRQHandler) { + CH_IRQ_PROLOGUE(); + + chSysLockFromIsr(); + events_flag_isr(EVT_MASK_BASEBAND); + chSysUnlockFromIsr(); + + creg::m0apptxevent::clear(); + + CH_IRQ_EPILOGUE(); +} + +} + +std::array baseband_buffer; +======= +static constexpr auto direction = baseband::Direction::Receive; +>>>>>>> upstream/master + +int main(void) { + init(); + + events_initialize(chThdSelf()); + m0apptxevent_interrupt_enable(); + + EventDispatcher event_dispatcher; + auto& message_handlers = event_dispatcher.message_handlers(); + + //const auto baseband_buffer = new std::array(); + + message_handlers.register_handler(Message::ID::BasebandConfiguration, + [&message_handlers](const Message* const p) { + auto message = reinterpret_cast(p); + if( message->configuration.mode != baseband_thread.baseband_configuration.mode ) { + + if( baseband_thread.baseband_processor ) { + i2s::i2s0::tx_mute(); + baseband::dma::disable(); + rf::rssi::stop(); + } + +<<<<<<< HEAD + switch(message->configuration.mode) { + case 1: + direction = baseband::Direction::Receive; + baseband_processor = new NarrowbandAMAudio(); + break; + + case 2: + direction = baseband::Direction::Receive; + baseband_processor = new NarrowbandFMAudio(); + break; + + case 3: + direction = baseband::Direction::Receive; + baseband_processor = new WidebandFMAudio(); + break; + + case 4: + direction = baseband::Direction::Receive; + baseband_processor = new FSKProcessor(message_handlers); + break; + + /*case 15: + direction = baseband::Direction::Transmit; + baseband_processor = new RDSProcessor(); + break;*/ + + case 16: + direction = baseband::Direction::Transmit; + baseband_processor = new LCRFSKProcessor(); + break; + + /*case 17: + direction = baseband::Direction::Transmit; + baseband_processor = new ToneProcessor(); + break;*/ + + case 18: + direction = baseband::Direction::Transmit; + baseband_processor = new JammerProcessor(); + break; +======= + // TODO: Timing problem around disabling DMA and nulling and deleting old processor + auto old_p = baseband_thread.baseband_processor; + baseband_thread.baseband_processor = nullptr; + delete old_p; + + switch(message->configuration.mode) { + case 0: + baseband_thread.baseband_processor = new NarrowbandAMAudio(); + break; + + case 1: + baseband_thread.baseband_processor = new NarrowbandFMAudio(); + break; + + case 2: + baseband_thread.baseband_processor = new WidebandFMAudio(); + break; +>>>>>>> upstream/master + + case 3: + baseband_thread.baseband_processor = new AISProcessor(); + break; + + case 4: + baseband_thread.baseband_processor = new WidebandSpectrum(); + break; + + case 5: + baseband_thread.baseband_processor = new TPMSProcessor(); + break; + + default: + break; + } + + if( baseband_thread.baseband_processor ) { + if( direction == baseband::Direction::Receive ) { + rf::rssi::start(); + } + baseband::dma::enable(direction); + } + } + + baseband_thread.baseband_configuration = message->configuration; + } +<<<<<<< HEAD + + baseband::dma::configure( + baseband_buffer.data(), + direction + ); +======= + ); +>>>>>>> upstream/master + + message_handlers.register_handler(Message::ID::Shutdown, + [&event_dispatcher](const Message* const) { + event_dispatcher.request_stop(); + } + ); + + /* TODO: Ensure DMAs are configured to point at first LLI in chain. */ + + rf::rssi::dma::allocate(4, 400); + + touch::dma::allocate(); + touch::dma::enable(); + + baseband::dma::configure( + baseband_buffer.data(), + direction + ); + //baseband::dma::allocate(4, 2048);d + + event_dispatcher.run(); + + shutdown(); + + ShutdownMessage shutdown_message; + shared_memory.application_queue.push(shutdown_message); + + halt(); + + return 0; +} diff --git a/firmware/baseband-tx/matched_filter.cpp b/firmware/baseband-tx/matched_filter.cpp new file mode 100644 index 00000000..5040349a --- /dev/null +++ b/firmware/baseband-tx/matched_filter.cpp @@ -0,0 +1,89 @@ +/* + * 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" + +namespace dsp { +namespace matched_filter { + +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 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 */ diff --git a/firmware/baseband-tx/matched_filter.hpp b/firmware/baseband-tx/matched_filter.hpp new file mode 100644 index 00000000..741f5018 --- /dev/null +++ b/firmware/baseband-tx/matched_filter.hpp @@ -0,0 +1,101 @@ +/* + * 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 "utility.hpp" + +#include + +#include +#include +#include + +#include +#include + +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; + using tap_t = std::complex; + + using taps_t = tap_t[]; + + template + MatchedFilter( + const T& taps, + size_t decimation_factor = 1 + ) { + configure(taps, decimation_factor); + } + + template + void configure( + const T& taps, + size_t decimation_factor + ) { + samples_ = std::make_unique(taps.size()); + taps_reversed_ = std::make_unique(taps.size()); + taps_count_ = taps.size(); + decimation_factor_ = decimation_factor; + std::reverse_copy(taps.cbegin(), taps.cend(), &taps_reversed_[0]); + } + + bool execute_once(const sample_t input); + + float get_output() const { + return output; + } + +private: + using samples_t = sample_t[]; + + std::unique_ptr samples_; + std::unique_ptr taps_reversed_; + size_t taps_count_ { 0 }; + size_t decimation_factor_ { 1 }; + size_t decimation_phase { 0 }; + float output; + + 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); + } +}; + +} /* namespace matched_filter */ +} /* namespace dsp */ + +#endif/*__MATCHED_FILTER_H__*/ diff --git a/firmware/baseband-tx/mcuconf.h b/firmware/baseband-tx/mcuconf.h new file mode 100755 index 00000000..eae37545 --- /dev/null +++ b/firmware/baseband-tx/mcuconf.h @@ -0,0 +1,47 @@ +/* + 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 \ No newline at end of file diff --git a/firmware/baseband-tx/name b/firmware/baseband-tx/name new file mode 100644 index 00000000..ff5e208f --- /dev/null +++ b/firmware/baseband-tx/name @@ -0,0 +1 @@ +Second module diff --git a/firmware/baseband-tx/packet_builder.cpp b/firmware/baseband-tx/packet_builder.cpp new file mode 100644 index 00000000..0fa5ec20 --- /dev/null +++ b/firmware/baseband-tx/packet_builder.cpp @@ -0,0 +1,22 @@ +/* + * 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" diff --git a/firmware/baseband-tx/packet_builder.hpp b/firmware/baseband-tx/packet_builder.hpp new file mode 100644 index 00000000..edf50357 --- /dev/null +++ b/firmware/baseband-tx/packet_builder.hpp @@ -0,0 +1,120 @@ +/* + * 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 +#include +#include +#include + +#include "bit_pattern.hpp" + +template +class PacketBuilder { +public: + using PayloadType = std::bitset<1024>; + using PayloadHandlerFunc = std::function; + + PacketBuilder( + const PreambleMatcher preamble_matcher, + const UnstuffMatcher unstuff_matcher, + const EndMatcher end_matcher, + const PayloadHandlerFunc payload_handler + ) : payload_handler { 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, bits_received) ) { + state = State::Payload; + } + break; + + case State::Payload: + if( !unstuff(bit_history, bits_received) ) { + payload[bits_received++] = symbol; + } + + if( end(bit_history, bits_received) ) { + payload_handler(payload, bits_received); + reset_state(); + } else { + if( packet_truncated() ) { + reset_state(); + } + } + break; + + default: + reset_state(); + break; + } + } + +private: + enum State { + Preamble, + Payload, + }; + + bool packet_truncated() const { + return bits_received >= payload.size(); + } + + const PayloadHandlerFunc payload_handler; + + BitHistory bit_history; + PreambleMatcher preamble; + UnstuffMatcher unstuff; + EndMatcher end; + + size_t bits_received { 0 }; + State state { State::Preamble }; + PayloadType payload; + + void reset_state() { + bits_received = 0; + state = State::Preamble; + } +}; + +#endif/*__PACKET_BUILDER_H__*/ diff --git a/firmware/baseband-tx/proc_fsk_lcr.cpp b/firmware/baseband-tx/proc_fsk_lcr.cpp new file mode 100644 index 00000000..478c21cd --- /dev/null +++ b/firmware/baseband-tx/proc_fsk_lcr.cpp @@ -0,0 +1,92 @@ +/* + * 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 "proc_fsk_lcr.hpp" +#include "portapack_shared_memory.hpp" + +#include + +void LCRFSKProcessor::execute(buffer_c8_t buffer) { + + for (size_t i = 0; i= 9) { + s = 0; + + if (sample_count >= shared_memory.afsk_samples_per_bit) { + if (shared_memory.afsk_transmit_done == false) + cur_byte = shared_memory.lcrdata[byte_pos]; + if (!cur_byte) { + if (shared_memory.afsk_repeat) { + shared_memory.afsk_repeat--; + bit_pos = 0; + byte_pos = 0; + cur_byte = shared_memory.lcrdata[0]; + message.n = shared_memory.afsk_repeat; + shared_memory.application_queue.push(message); + } else { + message.n = 0; + shared_memory.afsk_transmit_done = true; + shared_memory.application_queue.push(message); + cur_byte = 0; + } + } + + gbyte = 0; + gbyte = cur_byte << 1; + gbyte |= 1; + + cur_bit = (gbyte >> (9-bit_pos)) & 1; + + if (bit_pos == 9) { + bit_pos = 0; + byte_pos++; + } else { + bit_pos++; + } + + sample_count = 0; + } else { + sample_count++; + } + if (cur_bit) + aphase += shared_memory.afsk_phase_inc_mark; + else + aphase += shared_memory.afsk_phase_inc_space; + } else { + s++; + } + + //sample = sine_table_f32[(aphase & 0x00FF0000)>>16]; + + //FM + frq = sample * shared_memory.afsk_fmmod; + + phase = (phase + frq); + sphase = phase + (256<<16); + + //re = sine_table_f32[(sphase & 0x00FF0000)>>16]; + //im = sine_table_f32[(phase & 0x00FF0000)>>16]; + + buffer.p[i] = {(int8_t)re,(int8_t)im}; + } +} diff --git a/firmware/baseband-tx/proc_fsk_lcr.hpp b/firmware/baseband-tx/proc_fsk_lcr.hpp new file mode 100644 index 00000000..247a2a7f --- /dev/null +++ b/firmware/baseband-tx/proc_fsk_lcr.hpp @@ -0,0 +1,44 @@ +/* + * 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 __PROC_FSK_LCR_H__ +#define __PROC_FSK_LCR_H__ + +#include "baseband_processor.hpp" + +class LCRFSKProcessor : public BasebandProcessor { +public: + void execute(buffer_c8_t buffer) override; + +private: + int8_t re, im; + uint8_t s; + uint8_t bit_pos, byte_pos = 0; + char cur_byte = 0; + uint16_t gbyte; + uint8_t cur_bit = 0; + uint32_t sample_count; + uint32_t aphase, phase, sphase; + int32_t sample, sig, frq; + TXDoneMessage message; +}; + +#endif diff --git a/firmware/baseband-tx/proc_jammer.cpp b/firmware/baseband-tx/proc_jammer.cpp new file mode 100644 index 00000000..a8d4b0eb --- /dev/null +++ b/firmware/baseband-tx/proc_jammer.cpp @@ -0,0 +1,105 @@ +/* + * 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 "proc_jammer.hpp" +#include "portapack_shared_memory.hpp" +#include "sine_table.hpp" + +#include + +#define POLY_MASK_32 0xB4BCD35C + + +void JammerProcessor::execute(buffer_c8_t buffer) { + for (size_t i = 0; i 3000000) { + s = 0; + feedback = lfsr & 1; + lfsr >>= 1; + if (feedback == 1) + lfsr ^= POLY_MASK_32; + } else { + s++; + } + + aphase += lfsr;*/ + + /*if (s >= 10) { + s = 0; + aphase += 353205; // DEBUG + } else { + s++; + } + + sample = sintab[(aphase & 0x03FF0000)>>16];*/ + + // Duration timer + // + if (s >= 10000) { //shared_memory.jammer_ranges[ir].duration + s = 0; + for (;;) { + ir++; + if (ir > 15) ir = 0; + if (shared_memory.jammer_ranges[ir].active == true) break; + } + jammer_bw = shared_memory.jammer_ranges[ir].width / 4; + + message.freq = shared_memory.jammer_ranges[ir].center; + shared_memory.application_queue.push(message); + } else { + s++; + } + + // Ramp + /*if (r >= 10) { + if (sample < 128) + sample++; + else + sample = -127; + r = 0; + } else { + r++; + }*/ + + // Phase + if (r >= 70) { + aphase += ((aphase>>4) ^ 0x4573) << 14; + r = 0; + } else { + r++; + } + + aphase += 8830; + sample = sine_table_f32[(aphase & 0x00FF0000)>>16]; + + //FM + frq = sample * jammer_bw; // Bandwidth + + phase = (phase + frq); + sphase = phase + (256<<16); + + re = sine_table_f32[(sphase & 0x00FF0000)>>16]; + im = sine_table_f32[(phase & 0x00FF0000)>>16]; + + buffer.p[i] = {(int8_t)re,(int8_t)im}; + } +}; diff --git a/firmware/baseband-tx/proc_jammer.hpp b/firmware/baseband-tx/proc_jammer.hpp new file mode 100644 index 00000000..9a75940a --- /dev/null +++ b/firmware/baseband-tx/proc_jammer.hpp @@ -0,0 +1,44 @@ +/* + * 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 __PROC_JAMMER_H__ +#define __PROC_JAMMER_H__ + +#include "baseband_processor.hpp" + +class JammerProcessor : public BasebandProcessor { +public: + void execute(buffer_c8_t buffer) override; + +private: + int32_t lfsr32 = 0xABCDE; + uint32_t s; + int8_t r, ir, re, im; + int64_t jammer_bw, jammer_center; + int feedback; + int32_t lfsr; + uint32_t sample_count; + uint32_t aphase, phase, sphase; + int32_t sample, frq; + RetuneMessage message; +}; + +#endif diff --git a/firmware/baseband-tx/rssi.cpp b/firmware/baseband-tx/rssi.cpp new file mode 100644 index 00000000..2e9cfa32 --- /dev/null +++ b/firmware/baseband-tx/rssi.cpp @@ -0,0 +1,74 @@ +/* + * 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 + +#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 */ diff --git a/firmware/baseband-tx/rssi.hpp b/firmware/baseband-tx/rssi.hpp new file mode 100644 index 00000000..d340f338 --- /dev/null +++ b/firmware/baseband-tx/rssi.hpp @@ -0,0 +1,43 @@ +/* + * 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 +#include + +#include "buffer.hpp" + +namespace rf { +namespace rssi { + +using sample_t = uint8_t; +using buffer_t = buffer_t; + +void init(); +void start(); +void stop(); + +} /* namespace rssi */ +} /* namespace rf */ + +#endif/*__RSSI_H__*/ diff --git a/firmware/baseband-tx/rssi_dma.cpp b/firmware/baseband-tx/rssi_dma.cpp new file mode 100644 index 00000000..5b060604 --- /dev/null +++ b/firmware/baseband-tx/rssi_dma.cpp @@ -0,0 +1,182 @@ +/* + * 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 +#include +#include + +#include "hal.h" +#include "gpdma.hpp" + +using namespace lpc43xx; + +#include "portapack_dma.hpp" +#include "portapack_adc.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(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 Semaphore semaphore; +static volatile const gpdma::channel::LLI* next_lli = nullptr; + +static void transfer_complete() { + next_lli = gpdma_channel.next_lli(); + chSemSignalI(&semaphore); +} + +static void dma_error() { + disable(); +} + +void init() { + chSemInit(&semaphore, 0); + 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(&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(&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); + + chSemReset(&semaphore, 0); + gpdma_channel.enable(); +} + +bool is_enabled() { + return gpdma_channel.is_enabled(); +} + +void disable() { + gpdma_channel.disable_force(); +} + +rf::rssi::buffer_t wait_for_buffer() { + const auto status = chSemWait(&semaphore); + if( status == RDY_OK ) { + const auto next = next_lli; + if( next ) { + const size_t next_index = next - &lli[0]; + const size_t free_index = (next_index + buffers_config.count - 2) % buffers_config.count; + return { reinterpret_cast(lli[free_index].destaddr), buffers_config.items_per_buffer }; + } else { + return { nullptr, 0 }; + } + } else { + // TODO: Should I return here, or loop if RDY_RESET? + return { nullptr, 0 }; + } +} + +} /* namespace dma */ +} /* namespace rssi */ +} /* namespace rf */ diff --git a/firmware/baseband-tx/rssi_dma.hpp b/firmware/baseband-tx/rssi_dma.hpp new file mode 100644 index 00000000..e543dcf6 --- /dev/null +++ b/firmware/baseband-tx/rssi_dma.hpp @@ -0,0 +1,51 @@ +/* + * 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 + +#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__*/ diff --git a/firmware/baseband-tx/rssi_stats_collector.hpp b/firmware/baseband-tx/rssi_stats_collector.hpp new file mode 100644 index 00000000..6ab945bf --- /dev/null +++ b/firmware/baseband-tx/rssi_stats_collector.hpp @@ -0,0 +1,75 @@ +/* + * 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 +#include + +class RSSIStatisticsCollector { +public: + template + void process(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__*/ diff --git a/firmware/baseband-tx/symbol_coding.hpp b/firmware/baseband-tx/symbol_coding.hpp new file mode 100644 index 00000000..03a42455 --- /dev/null +++ b/firmware/baseband-tx/symbol_coding.hpp @@ -0,0 +1,44 @@ +/* + * 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 +#include + +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__*/ diff --git a/firmware/baseband-tx/touch_dma.cpp b/firmware/baseband-tx/touch_dma.cpp new file mode 100644 index 00000000..aa120ff3 --- /dev/null +++ b/firmware/baseband-tx/touch_dma.cpp @@ -0,0 +1,129 @@ +/* + * 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 +#include +#include + +#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(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(&LPC_ADC0->DR[0]); + lli.destaddr = reinterpret_cast(&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_force(); +} + +} /* namespace dma */ +} /* namespace touch */ diff --git a/firmware/baseband-tx/touch_dma.hpp b/firmware/baseband-tx/touch_dma.hpp new file mode 100644 index 00000000..777230e4 --- /dev/null +++ b/firmware/baseband-tx/touch_dma.hpp @@ -0,0 +1,52 @@ +/* + * 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 + +namespace touch { +namespace dma { + +using sample_t = uint32_t; +using buffer_t = buffer_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__*/ diff --git a/firmware/baseband.bin b/firmware/baseband.bin new file mode 100644 index 00000000..c972722a Binary files /dev/null and b/firmware/baseband.bin differ diff --git a/firmware/baseband/description b/firmware/baseband/description new file mode 100644 index 00000000..517d61c1 --- /dev/null +++ b/firmware/baseband/description @@ -0,0 +1 @@ +Basic RX/TX stuff for testing :) diff --git a/firmware/baseband/main.cpp b/firmware/baseband/main.cpp index 65a3d682..785e50a4 100755 --- a/firmware/baseband/main.cpp +++ b/firmware/baseband/main.cpp @@ -216,398 +216,6 @@ private: } }; - -static const int8_t sintab[1024] = { -0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 12, 13, 14, 15, 16, 16, 17, 18, 19, 19, 20, 21, 22, 22, 23, 24, 25, 26, 26, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36, 37, 38, 38, 39, 40, 41, 41, 42, 43, 44, 44, 45, 46, 46, 47, 48, 49, 49, 50, 51, 51, 52, 53, 54, 54, 55, 56, 56, 57, 58, 58, 59, 60, 61, 61, 62, 63, 63, 64, 65, 65, 66, 67, 67, 68, 69, 69, 70, 71, 71, 72, 72, 73, 74, 74, 75, 76, 76, 77, 78, 78, 79, 79, 80, 81, 81, 82, 82, 83, 84, 84, 85, 85, 86, 86, 87, 88, 88, 89, 89, 90, 90, 91, 91, 92, 93, 93, 94, 94, 95, 95, 96, 96, 97, 97, 98, 98, 99, 99, 100, 100, 101, 101, 102, 102, 102, 103, 103, 104, 104, 105, 105, 106, 106, 106, 107, 107, 108, 108, 109, 109, 109, 110, 110, 111, 111, 111, 112, 112, 112, 113, 113, 113, 114, 114, 114, 115, 115, 115, 116, 116, 116, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 120, 120, 120, 120, 121, 121, 121, 121, 122, 122, 122, 122, 122, 123, 123, 123, 123, 123, 124, 124, 124, 124, 124, 124, 125, 125, 125, 125, 125, 125, 125, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, -127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 126, 125, 125, 125, 125, 125, 125, 125, 124, 124, 124, 124, 124, 124, 123, 123, 123, 123, 123, 122, 122, 122, 122, 122, 121, 121, 121, 121, 120, 120, 120, 120, 119, 119, 119, 118, 118, 118, 118, 117, 117, 117, 116, 116, 116, 115, 115, 115, 114, 114, 114, 113, 113, 113, 112, 112, 112, 111, 111, 111, 110, 110, 109, 109, 109, 108, 108, 107, 107, 106, 106, 106, 105, 105, 104, 104, 103, 103, 102, 102, 102, 101, 101, 100, 100, 99, 99, 98, 98, 97, 97, 96, 96, 95, 95, 94, 94, 93, 93, 92, 91, 91, 90, 90, 89, 89, 88, 88, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81, 81, 80, 79, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 71, 70, 69, 69, 68, 67, 67, 66, 65, 65, 64, 63, 63, 62, 61, 61, 60, 59, 58, 58, 57, 56, 56, 55, 54, 54, 53, 52, 51, 51, 50, 49, 49, 48, 47, 46, 46, 45, 44, 44, 43, 42, 41, 41, 40, 39, 38, 38, 37, 36, 35, 35, 34, 33, 32, 32, 31, 30, 29, 29, 28, 27, 26, 26, 25, 24, 23, 22, 22, 21, 20, 19, 19, 18, 17, 16, 16, 15, 14, 13, 12, 12, 11, 10, 9, 9, 8, 7, 6, 5, 5, 4, 3, 2, 2, 1, 0, -1, -2, -2, -3, -4, -5, -5, -6, -7, -8, -9, -9, -10, -11, -12, -12, -13, -14, -15, -16, -16, -17, -18, -19, -19, -20, -21, -22, -22, -23, -24, -25, -26, -26, -27, -28, -29, -29, -30, -31, --32, -32, -33, -34, -35, -35, -36, -37, -38, -38, -39, -40, -41, -41, -42, -43, -44, -44, -45, -46, -46, -47, -48, -49, -49, -50, -51, -51, -52, -53, -54, -54, -55, -56, -56, -57, -58, -58, -59, -60, -61, -61, -62, -63, -63, -64, -65, -65, -66, -67, -67, -68, -69, -69, -70, -71, -71, -72, -72, -73, -74, -74, -75, -76, -76, -77, -78, -78, -79, -79, -80, -81, -81, -82, -82, -83, -84, -84, -85, -85, -86, -86, -87, -88, -88, -89, -89, -90, -90, -91, -91, -92, -93, -93, -94, -94, -95, -95, -96, -96, -97, -97, -98, -98, -99, -99, -100, -100, -101, -101, -102, -102, -102, -103, -103, -104, -104, -105, -105, -106, -106, -106, -107, -107, -108, -108, -109, -109, -109, -110, -110, -111, -111, -111, -112, -112, -112, -113, -113, -113, -114, -114, -114, -115, -115, -115, -116, -116, -116, -117, -117, -117, -118, -118, -118, -118, -119, -119, -119, -120, -120, -120, -120, -121, -121, -121, -121, -122, -122, -122, -122, -122, -123, -123, -123, -123, -123, -124, -124, -124, -124, -124, -124, -125, -125, -125, -125, -125, -125, -125, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, -127, --126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -126, -125, -125, -125, -125, -125, -125, -125, -124, -124, -124, -124, -124, -124, -123, -123, -123, -123, -123, -122, -122, -122, -122, -122, -121, -121, -121, -121, -120, -120, -120, -120, -119, -119, -119, -118, -118, -118, -118, -117, -117, -117, -116, -116, -116, -115, -115, -115, -114, -114, -114, -113, -113, -113, -112, -112, -112, -111, -111, -111, -110, -110, -109, -109, -109, -108, -108, -107, -107, -106, -106, -106, -105, -105, -104, -104, -103, -103, -102, -102, -102, -101, -101, -100, -100, -99, -99, -98, -98, -97, -97, -96, -96, -95, -95, -94, -94, -93, -93, -92, -91, -91, -90, -90, -89, -89, -88, -88, -87, -86, -86, -85, -85, -84, -84, -83, -82, -82, -81, -81, -80, -79, -79, -78, -78, -77, -76, -76, -75, -74, -74, -73, -72, -72, -71, -71, -70, -69, -69, -68, -67, -67, -66, -65, -65, -64, -63, -63, -62, -61, -61, -60, -59, -58, -58, -57, -56, -56, -55, -54, -54, -53, -52, -51, -51, -50, -49, -49, -48, -47, -46, -46, -45, -44, -44, -43, -42, -41, -41, -40, -39, -38, -38, -37, -36, -35, -35, -34, -33, -32, -32, -31, -30, -29, -29, -28, -27, -26, -26, -25, -24, -23, -22, -22, -21, -20, -19, -19, -18, -17, -16, -16, -15, -14, -13, -12, -12, -11, -10, -9, -9, -8, -7, -6, -5, -5, -4, -3, -2, -2, -1 -}; - -#define SAMPLES_PER_BIT 192 -#define FILTER_SIZE 576 -#define SAMPLE_BUFFER_SIZE SAMPLES_PER_BIT + FILTER_SIZE - -static int32_t waveform_biphase[] = { - 165,167,168,168,167,166,163,160, - 157,152,147,141,134,126,118,109, - 99,88,77,66,53,41,27,14, - 0,-14,-29,-44,-59,-74,-89,-105, - -120,-135,-150,-165,-179,-193,-206,-218, - -231,-242,-252,-262,-271,-279,-286,-291, - -296,-299,-301,-302,-302,-300,-297,-292, - -286,-278,-269,-259,-247,-233,-219,-202, - -185,-166,-145,-124,-101,-77,-52,-26, - 0,27,56,85,114,144,175,205, - 236,266,296,326,356,384,412,439, - 465,490,513,535,555,574,590,604, - 616,626,633,637,639,638,633,626, - 616,602,586,565,542,515,485,451, - 414,373,329,282,232,178,121,62, - 0,-65,-132,-202,-274,-347,-423,-500, - -578,-656,-736,-815,-894,-973,-1051,-1128, - -1203,-1276,-1347,-1415,-1479,-1540,-1596,-1648, - -1695,-1736,-1771,-1799,-1820,-1833,-1838,-1835, - -1822,-1800,-1767,-1724,-1670,-1605,-1527,-1437, - -1334,-1217,-1087,-943,-785,-611,-423,-219, - 0,235,487,755,1040,1341,1659,1994, - 2346,2715,3101,3504,3923,4359,4811,5280, - 5764,6264,6780,7310,7856,8415,8987,9573, - 10172,10782,11404,12036,12678,13329,13989,14656, - 15330,16009,16694,17382,18074,18767,19461,20155, - 20848,21539,22226,22909,23586,24256,24918,25571, - 26214,26845,27464,28068,28658,29231,29787,30325, - 30842,31339,31814,32266,32694,33097,33473,33823, - 34144,34437,34699,34931,35131,35299,35434,35535, - 35602,35634,35630,35591,35515,35402,35252,35065, - 34841,34579,34279,33941,33566,33153,32702,32214, - 31689,31128,30530,29897,29228,28525,27788,27017, - 26214,25379,24513,23617,22693,21740,20761,19755, - 18725,17672,16597,15501,14385,13251,12101,10935, - 9755,8563,7360,6148,4927,3701,2470,1235, - 0,-1235,-2470,-3701,-4927,-6148,-7360,-8563, - -9755,-10935,-12101,-13251,-14385,-15501,-16597,-17672, - -18725,-19755,-20761,-21740,-22693,-23617,-24513,-25379, - -26214,-27017,-27788,-28525,-29228,-29897,-30530,-31128, - -31689,-32214,-32702,-33153,-33566,-33941,-34279,-34579, - -34841,-35065,-35252,-35402,-35515,-35591,-35630,-35634, - -35602,-35535,-35434,-35299,-35131,-34931,-34699,-34437, - -34144,-33823,-33473,-33097,-32694,-32266,-31814,-31339, - -30842,-30325,-29787,-29231,-28658,-28068,-27464,-26845, - -26214,-25571,-24918,-24256,-23586,-22909,-22226,-21539, - -20848,-20155,-19461,-18767,-18074,-17382,-16694,-16009, - -15330,-14656,-13989,-13329,-12678,-12036,-11404,-10782, - -10172,-9573,-8987,-8415,-7856,-7310,-6780,-6264, - -5764,-5280,-4811,-4359,-3923,-3504,-3101,-2715, - -2346,-1994,-1659,-1341,-1040,-755,-487,-235, - 0,219,423,611,785,943,1087,1217, - 1334,1437,1527,1605,1670,1724,1767,1800, - 1822,1835,1838,1833,1820,1799,1771,1736, - 1695,1648,1596,1540,1479,1415,1347,1276, - 1203,1128,1051,973,894,815,736,656, - 578,500,423,347,274,202,132,65, - 0,-62,-121,-178,-232,-282,-329,-373, - -414,-451,-485,-515,-542,-565,-586,-602, - -616,-626,-633,-638,-639,-637,-633,-626, - -616,-604,-590,-574,-555,-535,-513,-490, - -465,-439,-412,-384,-356,-326,-296,-266, - -236,-205,-175,-144,-114,-85,-56,-27, - 0,26,52,77,101,124,145,166, - 185,202,219,233,247,259,269,278, - 286,292,297,300,302,302,301,299, - 296,291,286,279,271,262,252,242, - 231,218,206,193,179,165,150,135, - 120,105,89,74,59,44,29,14, - 0,-14,-27,-41,-53,-66,-77,-88, - -99,-109,-118,-126,-134,-141,-147,-152, - -157,-160,-163,-166,-167,-168,-168,-167 -}; - -/* -class RDSProcessor : public BasebandProcessor { -public: - void execute(buffer_c8_t buffer) override { - - for (size_t i = 0; i= 9) { - s = 0; - if(sample_count >= SAMPLES_PER_BIT) { - cur_bit = (shared_memory.rdsdata[(bit_pos / 26) & 15]>>(25-(bit_pos % 26))) & 1; - prev_output = cur_output; - cur_output = prev_output ^ cur_bit; - - int32_t *src = waveform_biphase; - int idx = in_sample_index; - - for(int j=0; j= SAMPLE_BUFFER_SIZE) idx = 0; - } - - in_sample_index += SAMPLES_PER_BIT; - if (in_sample_index >= SAMPLE_BUFFER_SIZE) in_sample_index -= SAMPLE_BUFFER_SIZE; - - bit_pos++; - sample_count = 0; - } - - sample = sample_buffer[out_sample_index]; - sample_buffer[out_sample_index] = 0; - out_sample_index++; - if (out_sample_index >= SAMPLE_BUFFER_SIZE) out_sample_index = 0; - - //AM @ 228k/4=57kHz - switch (mphase) { - case 0: - case 2: sample = 0; break; - case 1: break; - case 3: sample = -sample; break; - } - mphase++; - if (mphase >= 4) mphase = 0; - - sample_count++; - } else { - s++; - } - - //FM - frq = (sample>>16) * 386760; - - phase = (phase + frq); - sphase = phase + (256<<16); - - re = sintab[(sphase & 0x03FF0000)>>16]; - im = sintab[(phase & 0x03FF0000)>>16]; - - buffer.p[i] = {(int8_t)re,(int8_t)im}; - } - } - -private: - int8_t re, im; - uint8_t mphase, s; - uint32_t bit_pos; - int32_t sample_buffer[SAMPLE_BUFFER_SIZE] = {0}; - int32_t val; - uint8_t prev_output = 0; - uint8_t cur_output = 0; - uint8_t cur_bit = 0; - int sample_count = SAMPLES_PER_BIT; - int in_sample_index = 0; - int32_t sample; - int out_sample_index = SAMPLE_BUFFER_SIZE-1; - uint32_t phase, sphase; - int32_t sig, frq, frq_im, rdsc; - int32_t k; -};*/ - -class LCRFSKProcessor : public BasebandProcessor { -public: - void execute(buffer_c8_t buffer) override { - - for (size_t i = 0; i= 9) { - s = 0; - - if (sample_count >= shared_memory.afsk_samples_per_bit) { - if (shared_memory.afsk_transmit_done == false) - cur_byte = shared_memory.lcrdata[byte_pos]; - if (!cur_byte) { - if (shared_memory.afsk_repeat) { - shared_memory.afsk_repeat--; - bit_pos = 0; - byte_pos = 0; - cur_byte = shared_memory.lcrdata[0]; - message.n = shared_memory.afsk_repeat; - shared_memory.application_queue.push(message); - } else { - message.n = 0; - shared_memory.afsk_transmit_done = true; - shared_memory.application_queue.push(message); - cur_byte = 0; - } - } - - gbyte = 0; - gbyte = cur_byte << 1; - gbyte |= 1; - - cur_bit = (gbyte >> (9-bit_pos)) & 1; - - if (bit_pos == 9) { - bit_pos = 0; - byte_pos++; - } else { - bit_pos++; - } - - //aphase = 0x2FFFFFF; - - sample_count = 0; - } else { - sample_count++; - } - if (cur_bit) - aphase += shared_memory.afsk_phase_inc_mark; //(353205) - else - aphase += shared_memory.afsk_phase_inc_space; //(647542) - - sample = sintab[(aphase & 0x03FF0000)>>16]; - } else { - s++; - } - - sample = sintab[(aphase & 0x03FF0000)>>16]; - - //FM - frq = sample * shared_memory.afsk_fmmod; - - phase = (phase + frq); - sphase = phase + (256<<16); - - re = sintab[(sphase & 0x03FF0000)>>16]; - im = sintab[(phase & 0x03FF0000)>>16]; - - buffer.p[i] = {(int8_t)re,(int8_t)im}; - } - } - -private: - int8_t re, im; - uint8_t s; - uint8_t bit_pos, byte_pos = 0; - char cur_byte = 0; - uint16_t gbyte; - uint8_t cur_bit = 0; - uint32_t sample_count; - uint32_t aphase, phase, sphase; - int32_t sample, sig, frq; - TXDoneMessage message; -}; - -/*class ToneProcessor : public BasebandProcessor { -public: - void execute(buffer_c8_t buffer) override { - - for (size_t i = 0; i= 9) { - s = 0; - aphase += 353205; // DEBUG - sample = sintab[(aphase & 0x03FF0000)>>16]; - } else { - s++; - } - - sample = sintab[(aphase & 0x03FF0000)>>16]; - - //FM - frq = sample * 500; // DEBUG - - phase = (phase + frq); - sphase = phase + (256<<16); - - re = sintab[(sphase & 0x03FF0000)>>16]; - im = sintab[(phase & 0x03FF0000)>>16]; - - buffer.p[i] = {(int8_t)re,(int8_t)im}; - } - } - -private: - int8_t re, im; - uint8_t s; - uint32_t sample_count; - uint32_t aphase, phase, sphase; - int32_t sample, sig, frq; -};*/ - - -#define POLY_MASK_32 0xB4BCD35C - -class JammerProcessor : public BasebandProcessor { -public: - void execute(buffer_c8_t buffer) override { - - for (size_t i = 0; i 3000000) { - s = 0; - feedback = lfsr & 1; - lfsr >>= 1; - if (feedback == 1) - lfsr ^= POLY_MASK_32; - } else { - s++; - } - - aphase += lfsr;*/ - - /*if (s >= 10) { - s = 0; - aphase += 353205; // DEBUG - } else { - s++; - } - - sample = sintab[(aphase & 0x03FF0000)>>16];*/ - - // Duration timer - // - if (s >= 10000) { //shared_memory.jammer_ranges[ir].duration - s = 0; - for (;;) { - ir++; - if (ir > 15) ir = 0; - if (shared_memory.jammer_ranges[ir].active == true) break; - } - jammer_bw = shared_memory.jammer_ranges[ir].width; - - message.freq = shared_memory.jammer_ranges[ir].center; - shared_memory.application_queue.push(message); - } else { - s++; - } - - // Ramp - /*if (r >= 10) { - if (sample < 128) - sample++; - else - sample = -127; - r = 0; - } else { - r++; - }*/ - - // Phase - if (r >= 70) { - aphase += ((aphase>>4) ^ 0x4573) << 14; - r = 0; - } else { - r++; - } - - aphase += 35320; - sample = sintab[(aphase & 0x03FF0000)>>16]; - - //FM - frq = sample * jammer_bw; // Bandwidth - - //65536 -> 0.6M - //131072 -> 1.2M - - phase = (phase + frq); - sphase = phase + (256<<16); - - re = sintab[(sphase & 0x03FF0000)>>16]; - im = sintab[(phase & 0x03FF0000)>>16]; - - buffer.p[i] = {(int8_t)re,(int8_t)im}; - } - } - -private: - int32_t lfsr32 = 0xABCDE; - uint32_t s; - int8_t r, ir, re, im; - int64_t jammer_bw, jammer_center; - int feedback; - int32_t lfsr; - uint32_t sample_count; - uint32_t aphase, phase, sphase; - int32_t sample, frq; - RetuneMessage message; -}; - extern "C" { void __late_init(void) { @@ -736,6 +344,23 @@ private: const auto baseband_buffer = new std::array(); + +char ram_loop[32]; +typedef int (*fn_ptr)(void); +fn_ptr loop_ptr; + +void ram_loop_fn(void) { + while(1) {} +} + +void wait_for_switch(void) { + memcpy(&ram_loop[0], reinterpret_cast(&ram_loop_fn), 32); + loop_ptr = reinterpret_cast(&ram_loop[0]); + ReadyForSwitchMessage message { true }; + shared_memory.application_queue.push(message); + (*loop_ptr)(); + return; +} int main(void) { init(); @@ -791,26 +416,9 @@ int main(void) { direction = baseband::Direction::Receive; baseband_thread.baseband_processor = new TPMSProcessor(); break; - - /*case 15: - direction = baseband::Direction::Transmit; - baseband_thread.baseband_processor = new RDSProcessor(); - break;*/ - - case 16: - direction = baseband::Direction::Transmit; - baseband_thread.baseband_processor = new LCRFSKProcessor(); - break; - - /*case 17: - direction = baseband::Direction::Transmit; - baseband_thread.baseband_processor = new ToneProcessor(); - break;*/ - - case 18: - direction = baseband::Direction::Transmit; - baseband_thread.baseband_processor = new JammerProcessor(); - break; + + case 0xFF: + wait_for_switch(); default: break; diff --git a/firmware/baseband/name b/firmware/baseband/name new file mode 100644 index 00000000..8d194a1d --- /dev/null +++ b/firmware/baseband/name @@ -0,0 +1 @@ +First module diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index 20449e30..039660a0 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -49,6 +49,7 @@ public: TXDone = 9, SDCardStatus = 10, Retune = 11, + ReadyForSwitch = 12, MAX }; @@ -265,6 +266,17 @@ public: int n = 0; }; +class ReadyForSwitchMessage : public Message { +public: + ReadyForSwitchMessage( + bool ok + ) : Message { ID::ReadyForSwitch } + { + } + + const bool ok = false; +}; + class RetuneMessage : public Message { public: RetuneMessage( diff --git a/firmware/common/sine_table.hpp b/firmware/common/sine_table.hpp index 84979dfe..c749d008 100644 --- a/firmware/common/sine_table.hpp +++ b/firmware/common/sine_table.hpp @@ -39,7 +39,7 @@ constexpr size_t sine_table_f32_period_log2 = 8; constexpr size_t sine_table_f32_period = 1 << sine_table_f32_period_log2; constexpr uint32_t sine_table_f32_index_mask = sine_table_f32_period - 1; -constexpr std::array sine_table_f32 { { +static constexpr std::array sine_table_f32 { { 0.00000000e+00, 2.45412285e-02, 4.90676743e-02, 7.35645636e-02, 9.80171403e-02, 1.22410675e-01, 1.46730474e-01, 1.70961889e-01, 1.95090322e-01, diff --git a/firmware/common/ui.hpp b/firmware/common/ui.hpp index a61e3d08..89324944 100644 --- a/firmware/common/ui.hpp +++ b/firmware/common/ui.hpp @@ -79,6 +79,10 @@ struct Color { static constexpr Color blue() { return { 0, 0, 255 }; } + + static constexpr Color cyan() { + return { 0, 128, 255 }; + } static constexpr Color white() { return { 255, 255, 255 }; @@ -87,6 +91,10 @@ struct Color { static constexpr Color grey() { return { 127, 127, 127 }; } + + static constexpr Color purple() { + return { 204, 0, 102 }; + } }; #if 0 enum class CardinalDirection : uint8_t { diff --git a/firmware/portapack-h1-firmware.bin b/firmware/portapack-h1-firmware.bin index 9241323f..bd6b5d24 100644 Binary files a/firmware/portapack-h1-firmware.bin and b/firmware/portapack-h1-firmware.bin differ diff --git a/firmware/tools/make_baseband_file.py b/firmware/tools/make_baseband_file.py new file mode 100755 index 00000000..537bf898 --- /dev/null +++ b/firmware/tools/make_baseband_file.py @@ -0,0 +1,92 @@ +#!/usr/bin/env python + +# +# 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. +# + +import sys +import struct +import md5 + +usage_message = """ +Baseband processors modules package file generator + +Usage: ... +""" + +def read_image(path): + f = open(path, 'rb') + data = f.read() + f.close() + return data + +def write_file(data, path): + f = open(path, 'wb') + f.write(data) + f.close() + +if len(sys.argv) == 1: + print(usage_message) + sys.exit(-1) + +data = bytearray() +h_data = bytearray() +name = bytearray() +info = bytearray() +description = bytearray() +data_default_byte = bytearray((0,)) +m = md5.new() + +sys.argv = sys.argv[1:] + +# Format for module file: +# Magic (4), Version (2), Length (4), Name (16), MD5 (16), Description (214) +# Unpadded module binary... + +for args in sys.argv: + data = read_image(args + '/build/' + args + '.bin') + info = 'PPM ' + info += struct.pack('H', 1) + info += struct.pack('I', len(data)) + name = read_image(args + '/name') + if len(name) > 16: + name = name[0:15] + pad_size = 16 - len(name) + name += (data_default_byte * pad_size) + info += name + m.update(data) + digest = m.digest() + pad_size = 16 - len(digest) + digest += (data_default_byte * pad_size) + info += digest + description = read_image(args + '/description') + if len(description) > 214: + description = description[0:213] + pad_size = 214 - len(description) + description += (data_default_byte * pad_size) + info += description + data = info + data + write_file(data, args + '.bin') + md5sum = '' + for byte in digest: + md5sum += '0x' + format(byte, '02x') + ',' + h_data += 'const char md5_' + args.replace('-','_') + '[16] = {' + md5sum + '};\n' + +write_file(h_data, 'application/modules.h')