mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-10-01 01:26:06 -04:00
Dynamic baseband module loading from SD card
This commit is contained in:
parent
835d581e6c
commit
5f60b004f7
@ -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)
|
||||
|
@ -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 \
|
||||
|
@ -22,12 +22,14 @@
|
||||
#include "m4_startup.hpp"
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
#include "message.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
char * modhash;
|
||||
|
||||
/* TODO: OK, this is cool, but how do I put the M4 to sleep so I can switch to
|
||||
* a different image? Other than asking the old image to sleep while the M0
|
||||
* makes changes?
|
||||
@ -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<void*>(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<void*>(portapack::memory::map::m4_code.base()), mod_size, &bw);
|
||||
LPC_RGU->RESET_CTRL[0] = (1 << 13);
|
||||
f_close(&modfile);
|
||||
return 1;
|
||||
}
|
||||
f_close(&modfile);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void m4_switch(const char * hash) {
|
||||
modhash = const_cast<char*>(hash);
|
||||
|
||||
// Ask M4 to enter loop in RAM
|
||||
BasebandConfiguration baseband_switch {
|
||||
.mode = 0xFF,
|
||||
.sampling_rate = 0,
|
||||
.decimation_factor = 1,
|
||||
};
|
||||
|
||||
BasebandConfigurationMessage message { baseband_switch };
|
||||
shared_memory.baseband_queue.push(message);
|
||||
}
|
||||
|
||||
void m4_request_shutdown() {
|
||||
ShutdownMessage shutdown_message;
|
||||
shared_memory.baseband_queue.push(shutdown_message);
|
||||
|
@ -24,10 +24,14 @@
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#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__*/
|
||||
|
@ -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"
|
||||
|
2
firmware/application/modules.h
Normal file
2
firmware/application/modules.h
Normal file
@ -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,};
|
173
firmware/application/ui_alphanum.cpp
Normal file
173
firmware/application/ui_alphanum.cpp
Normal file
@ -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 <cstring>
|
||||
|
||||
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<Coord>((n % 5) * button_w),
|
||||
static_cast<Coord>((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);
|
||||
}
|
||||
|
||||
}
|
83
firmware/application/ui_alphanum.hpp
Normal file
83
firmware/application/ui_alphanum.hpp
Normal file
@ -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<void(char *)> 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<Button, 40> 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 */
|
@ -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 */
|
||||
|
@ -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 {
|
||||
|
@ -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<const TXDoneMessage*>(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();
|
||||
};
|
||||
|
89
firmware/application/ui_loadmodule.cpp
Normal file
89
firmware/application/ui_loadmodule.cpp
Normal file
@ -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 <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
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<const ReadyForSwitchMessage*>(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 */
|
56
firmware/application/ui_loadmodule.hpp
Normal file
56
firmware/application/ui_loadmodule.hpp
Normal file
@ -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 */
|
@ -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 }); } },
|
||||
} });
|
||||
}
|
||||
|
||||
|
@ -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<Coord>((n % 5) * button_w),
|
||||
static_cast<Coord>((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();
|
||||
|
@ -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 {
|
||||
|
||||
|
BIN
firmware/baseband-tx.bin
Normal file
BIN
firmware/baseband-tx.bin
Normal file
Binary file not shown.
275
firmware/baseband-tx/Makefile
Executable file
275
firmware/baseband-tx/Makefile
Executable file
@ -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
|
238
firmware/baseband-tx/audio_dma.cpp
Normal file
238
firmware/baseband-tx/audio_dma.cpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#include "hal.h"
|
||||
#include "gpdma.hpp"
|
||||
|
||||
using namespace lpc43xx;
|
||||
|
||||
#include "portapack_dma.hpp"
|
||||
|
||||
namespace audio {
|
||||
namespace dma {
|
||||
|
||||
constexpr uint32_t gpdma_ahb_master_peripheral = 1;
|
||||
constexpr uint32_t gpdma_ahb_master_memory = 0;
|
||||
constexpr uint32_t gpdma_ahb_master_lli_fetch = 0;
|
||||
|
||||
constexpr uint32_t gpdma_rx_peripheral = 0x9; /* I2S0 DMA request 1 */
|
||||
constexpr uint32_t gpdma_rx_src_peripheral = gpdma_rx_peripheral;
|
||||
constexpr uint32_t gpdma_rx_dest_peripheral = gpdma_rx_peripheral;
|
||||
|
||||
constexpr uint32_t gpdma_tx_peripheral = 0xa; /* I2S0 DMA request 2 */
|
||||
constexpr uint32_t gpdma_tx_src_peripheral = gpdma_tx_peripheral;
|
||||
constexpr uint32_t gpdma_tx_dest_peripheral = gpdma_tx_peripheral;
|
||||
|
||||
constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) {
|
||||
return {
|
||||
.lm = gpdma_ahb_master_lli_fetch,
|
||||
.r = 0,
|
||||
.lli = reinterpret_cast<uint32_t>(lli),
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Control control_tx(const size_t transfer_bytes) {
|
||||
return {
|
||||
.transfersize = gpdma::buffer_words(transfer_bytes, 4),
|
||||
.sbsize = 4, /* Burst size: 32 */
|
||||
.dbsize = 4, /* Burst size: 32 */
|
||||
.swidth = 2, /* Source transfer width: word (32 bits) */
|
||||
.dwidth = 2, /* Destination transfer width: word (32 bits) */
|
||||
.s = gpdma_ahb_master_memory,
|
||||
.d = gpdma_ahb_master_peripheral,
|
||||
.si = 1,
|
||||
.di = 0,
|
||||
.prot1 = 0,
|
||||
.prot2 = 0,
|
||||
.prot3 = 0,
|
||||
.i = 1,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Config config_tx() {
|
||||
return {
|
||||
.e = 0,
|
||||
.srcperipheral = gpdma_tx_src_peripheral,
|
||||
.destperipheral = gpdma_tx_dest_peripheral,
|
||||
.flowcntrl = gpdma::FlowControl::MemoryToPeripheral_DMAControl,
|
||||
.ie = 1,
|
||||
.itc = 1,
|
||||
.l = 0,
|
||||
.a = 0,
|
||||
.h = 0,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Control control_rx(const size_t transfer_bytes) {
|
||||
return {
|
||||
.transfersize = gpdma::buffer_words(transfer_bytes, 4),
|
||||
.sbsize = 4, /* Burst size: 32 */
|
||||
.dbsize = 4, /* Burst size: 32 */
|
||||
.swidth = 2, /* Source transfer width: word (32 bits) */
|
||||
.dwidth = 2, /* Destination transfer width: word (32 bits) */
|
||||
.s = gpdma_ahb_master_peripheral,
|
||||
.d = gpdma_ahb_master_memory,
|
||||
.si = 0,
|
||||
.di = 1,
|
||||
.prot1 = 0,
|
||||
.prot2 = 0,
|
||||
.prot3 = 0,
|
||||
.i = 1,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Config config_rx() {
|
||||
return {
|
||||
.e = 0,
|
||||
.srcperipheral = gpdma_rx_src_peripheral,
|
||||
.destperipheral = gpdma_rx_dest_peripheral,
|
||||
.flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl,
|
||||
.ie = 1,
|
||||
.itc = 1,
|
||||
.l = 0,
|
||||
.a = 0,
|
||||
.h = 0,
|
||||
};
|
||||
}
|
||||
|
||||
/* TODO: Clean up terminology around "buffer", "transfer", "samples" */
|
||||
|
||||
constexpr size_t buffer_samples_log2n = 7;
|
||||
constexpr size_t buffer_samples = (1 << buffer_samples_log2n);
|
||||
constexpr size_t transfers_per_buffer_log2n = 2;
|
||||
constexpr size_t transfers_per_buffer = (1 << transfers_per_buffer_log2n);
|
||||
constexpr size_t transfer_samples = buffer_samples / transfers_per_buffer;
|
||||
constexpr size_t transfers_mask = transfers_per_buffer - 1;
|
||||
|
||||
constexpr size_t buffer_bytes = buffer_samples * sizeof(sample_t);
|
||||
constexpr size_t transfer_bytes = transfer_samples * sizeof(sample_t);
|
||||
|
||||
static std::array<sample_t, buffer_samples> buffer_tx;
|
||||
static std::array<sample_t, buffer_samples> buffer_rx;
|
||||
|
||||
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_tx_loop;
|
||||
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_rx_loop;
|
||||
|
||||
static constexpr auto& gpdma_channel_i2s0_tx = gpdma::channels[portapack::i2s0_tx_gpdma_channel_number];
|
||||
static constexpr auto& gpdma_channel_i2s0_rx = gpdma::channels[portapack::i2s0_rx_gpdma_channel_number];
|
||||
|
||||
static volatile const gpdma::channel::LLI* tx_next_lli = nullptr;
|
||||
static volatile const gpdma::channel::LLI* rx_next_lli = nullptr;
|
||||
|
||||
static void tx_transfer_complete() {
|
||||
tx_next_lli = gpdma_channel_i2s0_tx.next_lli();
|
||||
}
|
||||
|
||||
static void tx_error() {
|
||||
disable();
|
||||
}
|
||||
|
||||
static void rx_transfer_complete() {
|
||||
rx_next_lli = gpdma_channel_i2s0_rx.next_lli();
|
||||
}
|
||||
|
||||
static void rx_error() {
|
||||
disable();
|
||||
}
|
||||
|
||||
void init() {
|
||||
gpdma_channel_i2s0_tx.set_handlers(tx_transfer_complete, tx_error);
|
||||
gpdma_channel_i2s0_rx.set_handlers(rx_transfer_complete, rx_error);
|
||||
|
||||
// LPC_GPDMA->SYNC |= (1 << gpdma_rx_peripheral);
|
||||
// LPC_GPDMA->SYNC |= (1 << gpdma_tx_peripheral);
|
||||
}
|
||||
|
||||
static void configure_tx() {
|
||||
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->TXFIFO);
|
||||
const auto control_value = control_tx(transfer_bytes);
|
||||
for(size_t i=0; i<lli_tx_loop.size(); i++) {
|
||||
const auto memory = reinterpret_cast<uint32_t>(&buffer_tx[i * transfer_samples]);
|
||||
lli_tx_loop[i].srcaddr = memory;
|
||||
lli_tx_loop[i].destaddr = peripheral;
|
||||
lli_tx_loop[i].lli = lli_pointer(&lli_tx_loop[(i + 1) % lli_tx_loop.size()]);
|
||||
lli_tx_loop[i].control = control_value;
|
||||
}
|
||||
}
|
||||
|
||||
static void configure_rx() {
|
||||
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->RXFIFO);
|
||||
const auto control_value = control_rx(transfer_bytes);
|
||||
for(size_t i=0; i<lli_rx_loop.size(); i++) {
|
||||
const auto memory = reinterpret_cast<uint32_t>(&buffer_rx[i * transfer_samples]);
|
||||
lli_rx_loop[i].srcaddr = peripheral;
|
||||
lli_rx_loop[i].destaddr = memory;
|
||||
lli_rx_loop[i].lli = lli_pointer(&lli_rx_loop[(i + 1) % lli_rx_loop.size()]);
|
||||
lli_rx_loop[i].control = control_value;
|
||||
}
|
||||
}
|
||||
|
||||
void configure() {
|
||||
configure_tx();
|
||||
configure_rx();
|
||||
}
|
||||
|
||||
void enable() {
|
||||
const auto gpdma_config_tx = config_tx();
|
||||
const auto gpdma_config_rx = config_rx();
|
||||
|
||||
gpdma_channel_i2s0_tx.configure(lli_tx_loop[0], gpdma_config_tx);
|
||||
gpdma_channel_i2s0_rx.configure(lli_rx_loop[0], gpdma_config_rx);
|
||||
|
||||
gpdma_channel_i2s0_tx.enable();
|
||||
gpdma_channel_i2s0_rx.enable();
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel_i2s0_tx.disable_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<sample_t*>(lli_tx_loop[free_index].srcaddr), transfer_samples };
|
||||
} else {
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
buffer_t rx_empty_buffer() {
|
||||
const auto next_lli = rx_next_lli;
|
||||
if( next_lli ) {
|
||||
const size_t next_index = next_lli - &lli_rx_loop[0];
|
||||
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
|
||||
return { reinterpret_cast<sample_t*>(lli_rx_loop[free_index].srcaddr), transfer_samples };
|
||||
} else {
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace dma */
|
||||
} /* namespace audio */
|
43
firmware/baseband-tx/audio_dma.hpp
Normal file
43
firmware/baseband-tx/audio_dma.hpp
Normal file
@ -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 <cstdint>
|
||||
|
||||
#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__*/
|
95
firmware/baseband-tx/audio_stats_collector.hpp
Normal file
95
firmware/baseband-tx/audio_stats_collector.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
class AudioStatsCollector {
|
||||
public:
|
||||
template<typename Callback>
|
||||
void feed(buffer_s16_t src, Callback callback) {
|
||||
consume_audio_buffer(src);
|
||||
|
||||
if( update_stats(src.count, src.sampling_rate) ) {
|
||||
callback(statistics);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
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__*/
|
203
firmware/baseband-tx/baseband_dma.cpp
Normal file
203
firmware/baseband-tx/baseband_dma.cpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#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<uint32_t>(lli),
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Control control(const baseband::Direction direction, const size_t buffer_words) {
|
||||
return {
|
||||
.transfersize = buffer_words,
|
||||
.sbsize = 0, /* Burst size: 1 */
|
||||
.dbsize = 0, /* Burst size: 1 */
|
||||
.swidth = 2, /* Source transfer width: word (32 bits) */
|
||||
.dwidth = 2, /* Destination transfer width: word (32 bits) */
|
||||
.s = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_memory : gpdma_ahb_master_sgpio,
|
||||
.d = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_sgpio : gpdma_ahb_master_memory,
|
||||
.si = (direction == baseband::Direction::Transmit) ? 1U : 0U,
|
||||
.di = (direction == baseband::Direction::Transmit) ? 0U : 1U,
|
||||
.prot1 = 0,
|
||||
.prot2 = 0,
|
||||
.prot3 = 0,
|
||||
.i = 1,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Config config(const baseband::Direction direction) {
|
||||
return {
|
||||
.e = 0,
|
||||
.srcperipheral = gpdma_src_peripheral,
|
||||
.destperipheral = gpdma_dest_peripheral,
|
||||
.flowcntrl = (direction == baseband::Direction::Transmit)
|
||||
? gpdma::FlowControl::MemoryToPeripheral_DMAControl
|
||||
: gpdma::FlowControl::PeripheralToMemory_DMAControl,
|
||||
.ie = 1,
|
||||
.itc = 1,
|
||||
.l = 0,
|
||||
.a = 0,
|
||||
.h = 0,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr size_t buffer_samples_log2n = 13;
|
||||
constexpr size_t buffer_samples = (1 << buffer_samples_log2n);
|
||||
constexpr size_t transfers_per_buffer_log2n = 2;
|
||||
constexpr size_t transfers_per_buffer = (1 << transfers_per_buffer_log2n);
|
||||
constexpr size_t transfer_samples = buffer_samples / transfers_per_buffer;
|
||||
constexpr size_t transfers_mask = transfers_per_buffer - 1;
|
||||
|
||||
constexpr size_t buffer_bytes = buffer_samples * sizeof(baseband::sample_t);
|
||||
constexpr size_t transfer_bytes = transfer_samples * sizeof(baseband::sample_t);
|
||||
|
||||
constexpr size_t msg_count = transfers_per_buffer - 1;
|
||||
|
||||
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_loop;
|
||||
static constexpr auto& gpdma_channel_sgpio = gpdma::channels[portapack::sgpio_gpdma_channel_number];
|
||||
|
||||
//static Mailbox mailbox;
|
||||
//static std::array<msg_t, msg_count> 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<uint32_t>(&LPC_SGPIO->REG_SS[0]);
|
||||
const auto control_value = control(direction, gpdma::buffer_words(transfer_bytes, 4));
|
||||
for(size_t i=0; i<lli_loop.size(); i++) {
|
||||
const auto memory = reinterpret_cast<uint32_t>(&buffer_base[i * transfer_samples]);
|
||||
lli_loop[i].srcaddr = (direction == Direction::Transmit) ? memory : peripheral;
|
||||
lli_loop[i].destaddr = (direction == Direction::Transmit) ? peripheral : memory;
|
||||
lli_loop[i].lli = lli_pointer(&lli_loop[(i + 1) % lli_loop.size()]);
|
||||
lli_loop[i].control = control_value;
|
||||
}
|
||||
}
|
||||
|
||||
void enable(const baseband::Direction direction) {
|
||||
const auto gpdma_config = config(direction);
|
||||
gpdma_channel_sgpio.configure(lli_loop[0], gpdma_config);
|
||||
|
||||
//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<sample_t*>(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<sample_t*>(lli_loop[free_index].srcaddr), transfer_samples };
|
||||
} else {
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
} else {
|
||||
return { nullptr, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace dma */
|
||||
} /* namespace baseband */
|
53
firmware/baseband-tx/baseband_dma.hpp
Normal file
53
firmware/baseband-tx/baseband_dma.hpp
Normal file
@ -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 <cstddef>
|
||||
#include <array>
|
||||
|
||||
#include "complex.hpp"
|
||||
#include "baseband.hpp"
|
||||
|
||||
namespace baseband {
|
||||
namespace dma {
|
||||
|
||||
using Handler = void (*)();
|
||||
|
||||
void init();
|
||||
void configure(
|
||||
baseband::sample_t* const buffer_base,
|
||||
const baseband::Direction direction
|
||||
);
|
||||
|
||||
void enable(const baseband::Direction direction);
|
||||
bool is_enabled();
|
||||
|
||||
void disable();
|
||||
|
||||
baseband::buffer_t wait_for_rx_buffer();
|
||||
baseband::buffer_t wait_for_tx_buffer();
|
||||
|
||||
} /* namespace dma */
|
||||
} /* namespace baseband */
|
||||
|
||||
#endif/*__BASEBAND_DMA_H__*/
|
122
firmware/baseband-tx/baseband_processor.cpp
Normal file
122
firmware/baseband-tx/baseband_processor.cpp
Normal file
@ -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 <cstddef>
|
||||
#include <algorithm>
|
||||
|
||||
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<spectrum_message.spectrum.db.size(); i++) {
|
||||
const auto mag2 = magnitude_squared(channel_spectrum[i]);
|
||||
const float db = complex16_mag_squared_to_dbv_norm(mag2);
|
||||
constexpr float mag_scale = 5.0f;
|
||||
const unsigned int v = (db * mag_scale) + 255.0f;
|
||||
spectrum_message.spectrum.db[i] = std::max(0U, std::min(255U, v));
|
||||
}
|
||||
|
||||
/* TODO: Rename .db -> .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; i<audio_buffer.count; i++) {
|
||||
audio_buffer.p[i].left = audio_buffer.p[i].right = audio.p[i];
|
||||
}
|
||||
i2s::i2s0::tx_unmute();
|
||||
|
||||
feed_audio_stats(audio);
|
||||
}
|
||||
|
||||
void BasebandProcessor::post_channel_stats_message(const ChannelStatistics statistics) {
|
||||
channel_stats_message.statistics = statistics;
|
||||
shared_memory.application_queue.push(channel_stats_message);
|
||||
}
|
||||
|
||||
void BasebandProcessor::post_channel_spectrum_message(const buffer_c16_t data) {
|
||||
if( !channel_spectrum_request_update ) {
|
||||
fft_swap(data, channel_spectrum);
|
||||
channel_spectrum_sampling_rate = data.sampling_rate;
|
||||
channel_spectrum_request_update = true;
|
||||
events_flag(EVT_MASK_SPECTRUM);
|
||||
}
|
||||
}
|
||||
|
||||
void BasebandProcessor::feed_audio_stats(const buffer_s16_t audio) {
|
||||
audio_stats.feed(
|
||||
audio,
|
||||
[this](const AudioStatistics statistics) {
|
||||
this->post_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);
|
||||
}
|
76
firmware/baseband-tx/baseband_processor.hpp
Normal file
76
firmware/baseband-tx/baseband_processor.hpp
Normal file
@ -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 <array>
|
||||
#include <cstdint>
|
||||
#include <complex>
|
||||
|
||||
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<std::complex<float>, 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__*/
|
96
firmware/baseband-tx/baseband_stats_collector.hpp
Normal file
96
firmware/baseband-tx/baseband_stats_collector.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
class BasebandStatsCollector {
|
||||
public:
|
||||
BasebandStatsCollector(
|
||||
const Thread* const thread_idle,
|
||||
const Thread* const thread_main,
|
||||
const Thread* const thread_rssi,
|
||||
const Thread* const thread_baseband
|
||||
) : thread_idle { thread_idle },
|
||||
thread_main { thread_main },
|
||||
thread_rssi { thread_rssi },
|
||||
thread_baseband { thread_baseband }
|
||||
{
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void process(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__*/
|
100
firmware/baseband-tx/block_decimator.hpp
Normal file
100
firmware/baseband-tx/block_decimator.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "complex.hpp"
|
||||
|
||||
template<size_t N>
|
||||
class BlockDecimator {
|
||||
public:
|
||||
constexpr BlockDecimator(
|
||||
const size_t factor
|
||||
) : factor_ { factor }
|
||||
{
|
||||
}
|
||||
|
||||
void set_input_sampling_rate(const uint32_t new_sampling_rate) {
|
||||
if( new_sampling_rate != input_sampling_rate() ) {
|
||||
input_sampling_rate_ = new_sampling_rate;
|
||||
reset_state();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t input_sampling_rate() const {
|
||||
return input_sampling_rate_;
|
||||
}
|
||||
|
||||
void set_factor(const size_t new_factor) {
|
||||
if( new_factor != factor() ) {
|
||||
factor_ = new_factor;
|
||||
reset_state();
|
||||
}
|
||||
}
|
||||
|
||||
size_t factor() const {
|
||||
return factor_;
|
||||
}
|
||||
|
||||
uint32_t output_sampling_rate() const {
|
||||
return input_sampling_rate() / factor();
|
||||
}
|
||||
|
||||
template<typename BlockCallback>
|
||||
void feed(const buffer_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<complex16_t, N> buffer;
|
||||
uint32_t input_sampling_rate_ { 0 };
|
||||
size_t factor_ { 1 };
|
||||
size_t src_i { 0 };
|
||||
size_t dst_i { 0 };
|
||||
|
||||
void reset_state() {
|
||||
src_i = 0;
|
||||
dst_i = 0;
|
||||
}
|
||||
};
|
||||
|
||||
#endif/*__BLOCK_DECIMATOR_H__*/
|
84
firmware/baseband-tx/channel_decimator.cpp
Normal file
84
firmware/baseband-tx/channel_decimator.cpp
Normal file
@ -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<int8_t>[2048], [-128, 127]
|
||||
* -> Shift by -fs/4
|
||||
* -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs
|
||||
* -0.1dB @ 86kHz, -1dB @ 270kHz, -60dB @ 1.44MHz
|
||||
* -> gain of 256
|
||||
* -> decimation by 2
|
||||
* -> 1.544MHz complex<int16_t>[1024], [-32768, 32512] */
|
||||
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<int16_t>[1024], [-32768, 32512]
|
||||
* -> 3rd order CIC: -0.1dB @ 0.028fs, -1dB @ 0.088fs, -60dB @ 0.468fs
|
||||
* -0.1dB @ 43kHz, -1dB @ 136kHz, -60dB @ 723kHz
|
||||
* -> gain of 8
|
||||
* -> decimation by 2
|
||||
* -> 768kHz complex<int16_t>[512], [-8192, 8128] */
|
||||
auto cic_1_out = cic_1.execute(stage_0_out, work_baseband_buffer);
|
||||
if( decimation_factor == DecimationFactor::By4 ) {
|
||||
return cic_1_out;
|
||||
}
|
||||
|
||||
/* 768kHz complex<int16_t>[512], [-32768, 32512]
|
||||
* -> 3rd order CIC decimation by 2, gain of 1
|
||||
* -> 384kHz complex<int16_t>[256], [-32768, 32512] */
|
||||
auto cic_2_out = cic_2.execute(cic_1_out, work_baseband_buffer);
|
||||
if( decimation_factor == DecimationFactor::By8 ) {
|
||||
return cic_2_out;
|
||||
}
|
||||
|
||||
/* 384kHz complex<int16_t>[256], [-32768, 32512]
|
||||
* -> 3rd order CIC decimation by 2, gain of 1
|
||||
* -> 192kHz complex<int16_t>[128], [-32768, 32512] */
|
||||
auto cic_3_out = cic_3.execute(cic_2_out, work_baseband_buffer);
|
||||
if( decimation_factor == DecimationFactor::By16 ) {
|
||||
return cic_3_out;
|
||||
}
|
||||
|
||||
/* 192kHz complex<int16_t>[128], [-32768, 32512]
|
||||
* -> 3rd order CIC decimation by 2, gain of 1
|
||||
* -> 96kHz complex<int16_t>[64], [-32768, 32512] */
|
||||
auto cic_4_out = cic_4.execute(cic_3_out, work_baseband_buffer);
|
||||
|
||||
return cic_4_out;
|
||||
}
|
79
firmware/baseband-tx/channel_decimator.hpp
Normal file
79
firmware/baseband-tx/channel_decimator.hpp
Normal file
@ -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 <array>
|
||||
|
||||
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<complex16_t, 1024> 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__*/
|
66
firmware/baseband-tx/channel_stats_collector.hpp
Normal file
66
firmware/baseband-tx/channel_stats_collector.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include <hal.h>
|
||||
|
||||
class ChannelStatsCollector {
|
||||
public:
|
||||
template<typename Callback>
|
||||
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__*/
|
546
firmware/baseband-tx/chconf.h
Executable file
546
firmware/baseband-tx/chconf.h
Executable file
@ -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_ */
|
||||
|
||||
/** @} */
|
22
firmware/baseband-tx/clock_recovery.cpp
Normal file
22
firmware/baseband-tx/clock_recovery.cpp
Normal file
@ -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"
|
178
firmware/baseband-tx/clock_recovery.hpp
Normal file
178
firmware/baseband-tx/clock_recovery.hpp
Normal file
@ -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 <cstddef>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
|
||||
#include "linear_resampler.hpp"
|
||||
|
||||
namespace clock_recovery {
|
||||
|
||||
class GardnerTimingErrorDetector {
|
||||
public:
|
||||
static constexpr size_t samples_per_symbol { 2 };
|
||||
|
||||
/*
|
||||
Expects retimed samples at a rate of twice the expected symbol rate.
|
||||
Calculates timing error, sends symbol and error to handler.
|
||||
*/
|
||||
template<typename SymbolHandler>
|
||||
void operator()(
|
||||
const float in,
|
||||
SymbolHandler symbol_handler
|
||||
) {
|
||||
/* NOTE: Algorithm is sensitive to input magnitude. Timing error value
|
||||
* will scale proportionally. Best practice is to use error sign only.
|
||||
*/
|
||||
t[2] = t[1];
|
||||
t[1] = t[0];
|
||||
t[0] = in;
|
||||
|
||||
if( symbol_phase == 0 ) {
|
||||
const auto symbol = t[0];
|
||||
const float lateness = (t[0] - t[2]) * t[1];
|
||||
symbol_handler(symbol, lateness);
|
||||
}
|
||||
|
||||
symbol_phase = (symbol_phase + 1) % samples_per_symbol;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<float, 3> t { { 0.0f, 0.0f, 0.0f } };
|
||||
size_t symbol_phase { 0 };
|
||||
};
|
||||
|
||||
class LinearErrorFilter {
|
||||
public:
|
||||
LinearErrorFilter(
|
||||
const float filter_alpha = 0.95f,
|
||||
const float error_weight = -1.0f
|
||||
) : filter_alpha { filter_alpha },
|
||||
error_weight { error_weight }
|
||||
{
|
||||
}
|
||||
|
||||
float operator()(
|
||||
const float error
|
||||
) {
|
||||
error_filtered = filter_alpha * error_filtered + (1.0f - filter_alpha) * error;
|
||||
return error_filtered * error_weight;
|
||||
}
|
||||
|
||||
private:
|
||||
const float filter_alpha;
|
||||
const float error_weight;
|
||||
float error_filtered { 0.0f };
|
||||
};
|
||||
|
||||
class FixedErrorFilter {
|
||||
public:
|
||||
FixedErrorFilter(
|
||||
) {
|
||||
}
|
||||
|
||||
FixedErrorFilter(
|
||||
const float weight
|
||||
) : weight_ { weight }
|
||||
{
|
||||
}
|
||||
|
||||
float operator()(
|
||||
const float lateness
|
||||
) const {
|
||||
return (lateness < 0.0f) ? weight() : -weight();
|
||||
}
|
||||
|
||||
float weight() const {
|
||||
return weight_;
|
||||
}
|
||||
|
||||
private:
|
||||
float weight_ { 1.0f / 16.0f };
|
||||
};
|
||||
|
||||
template<typename ErrorFilter>
|
||||
class ClockRecovery {
|
||||
public:
|
||||
ClockRecovery(
|
||||
const float sampling_rate,
|
||||
const float symbol_rate,
|
||||
ErrorFilter error_filter,
|
||||
std::function<void(const float)> symbol_handler
|
||||
) : symbol_handler { symbol_handler }
|
||||
{
|
||||
configure(sampling_rate, symbol_rate, error_filter);
|
||||
}
|
||||
|
||||
ClockRecovery(
|
||||
std::function<void(const float)> 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<void(const float)> 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__*/
|
1
firmware/baseband-tx/description
Normal file
1
firmware/baseband-tx/description
Normal file
@ -0,0 +1 @@
|
||||
More specific stuff for testing :o
|
403
firmware/baseband-tx/dsp_decimate.cpp
Normal file
403
firmware/baseband-tx/dsp_decimate.cpp
Normal file
@ -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 <hal.h>
|
||||
|
||||
namespace dsp {
|
||||
namespace decimate {
|
||||
|
||||
buffer_c16_t TranslateByFSOver4AndDecimateBy2CIC3::execute(buffer_c8_t src, buffer_c16_t dst) {
|
||||
/* Translates incoming complex<int8_t> samples by -fs/4,
|
||||
* decimates by two using a non-recursive third-order CIC filter.
|
||||
*/
|
||||
|
||||
/* Derivation of algorithm:
|
||||
* Original CIC filter (decimating by two):
|
||||
* D_I0 = i3 * 1 + i2 * 3 + i1 * 3 + i0 * 1
|
||||
* D_Q0 = q3 * 1 + q2 * 3 + q1 * 3 + q0 * 1
|
||||
*
|
||||
* D_I1 = i5 * 1 + i4 * 3 + i3 * 3 + i2 * 1
|
||||
* D_Q1 = q5 * 1 + q4 * 3 + q3 * 3 + q2 * 1
|
||||
*
|
||||
* Translate -fs/4, phased 180 degrees, accomplished by complex multiplication
|
||||
* of complex length-4 sequence:
|
||||
*
|
||||
* Substitute:
|
||||
* i0 = -i0, q0 = -q0
|
||||
* i1 = -q1, q1 = i1
|
||||
* i2 = i2, q2 = q2
|
||||
* i3 = q3, q3 = -i3
|
||||
* i4 = -i4, q4 = -q4
|
||||
* i5 = -q5, q5 = i5
|
||||
*
|
||||
* Resulting taps (with decimation by 2, four samples in, two samples out):
|
||||
* D_I0 = q3 * 1 + i2 * 3 + -q1 * 3 + -i0 * 1
|
||||
* D_Q0 = -i3 * 1 + q2 * 3 + i1 * 3 + -q0 * 1
|
||||
*
|
||||
* D_I1 = -q5 * 1 + -i4 * 3 + q3 * 3 + i2 * 1
|
||||
* D_Q1 = i5 * 1 + -q4 * 3 + -i3 * 3 + q2 * 1
|
||||
*/
|
||||
|
||||
// 6 cycles per complex input sample, not including loop overhead.
|
||||
uint32_t q1_i0 = _q1_i0;
|
||||
uint32_t q0_i1 = _q0_i1;
|
||||
/* 3:1 Scaled by 32 to normalize output to +/-32768-ish. */
|
||||
constexpr uint32_t scale_factor = 32;
|
||||
const uint32_t k_3_1 = 0x00030001 * scale_factor;
|
||||
uint32_t* src_p = reinterpret_cast<uint32_t*>(&src.p[0]);
|
||||
uint32_t* const src_end = reinterpret_cast<uint32_t*>(&src.p[src.count]);
|
||||
uint32_t* dst_p = reinterpret_cast<uint32_t*>(&dst.p[0]);
|
||||
while(src_p < src_end) {
|
||||
const uint32_t q3_i3_q2_i2 = *(src_p++); // 3
|
||||
const uint32_t q5_i5_q4_i4 = *(src_p++);
|
||||
|
||||
const uint32_t i2_i3 = __SXTB16(q3_i3_q2_i2, 16); // 1: (q3_i3_q2_i2 ror 16)[23:16]:(q3_i3_q2_i2 ror 16)[7:0]
|
||||
const uint32_t q3_q2 = __SXTB16(q3_i3_q2_i2, 8); // 1: (q3_i3_q2_i2 ror 8)[23:16]:(q3_i3_q2_i2 ror 8)[7:0]
|
||||
const uint32_t i2_q3 = __PKHTB(i2_i3, q3_q2, 16); // 1: Rn[31:16]:(Rm>>16)[15:0]
|
||||
const uint32_t i3_q2 = __PKHBT(q3_q2, i2_i3, 16); // 1:(Rm<<16)[31:16]:Rn[15:0]
|
||||
|
||||
// D_I0 = 3 * (i2 - q1) + (q3 - i0)
|
||||
const uint32_t i2_m_q1_q3_m_i0 = __QSUB16(i2_q3, q1_i0); // 1: Rn[31:16]-Rm[31:16]:Rn[15:0]-Rm[15:0]
|
||||
const uint32_t d_i0 = __SMUAD(k_3_1, i2_m_q1_q3_m_i0); // 1: Rm[15:0]*Rs[15:0]+Rm[31:16]*Rs[31:16]
|
||||
|
||||
// D_Q0 = 3 * (q2 + i1) - (i3 + q0)
|
||||
const uint32_t i3_p_q0_q2_p_i1 = __QADD16(i3_q2, q0_i1); // 1: Rn[31:16]+Rm[31:16]:Rn[15:0]+Rm[15:0]
|
||||
const uint32_t d_q0 = __SMUSDX(i3_p_q0_q2_p_i1, k_3_1); // 1: Rm[15:0]*Rs[31:16]–Rm[31:16]*RsX[15:0]
|
||||
const uint32_t d_q0_i0 = __PKHBT(d_i0, d_q0, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
|
||||
|
||||
const uint32_t i5_i4 = __SXTB16(q5_i5_q4_i4, 0); // 1: (q5_i5_q4_i4 ror 0)[23:16]:(q5_i5_q4_i4 ror 0)[7:0]
|
||||
const uint32_t q4_q5 = __SXTB16(q5_i5_q4_i4, 24); // 1: (q5_i5_q4_i4 ror 24)[23:16]:(q5_i5_q4_i4 ror 24)[7:0]
|
||||
const uint32_t q4_i5 = __PKHTB(q4_q5, i5_i4, 16); // 1: Rn[31:16]:(Rm>>16)[15:0]
|
||||
const uint32_t q5_i4 = __PKHBT(i5_i4, q4_q5, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
|
||||
|
||||
// D_I1 = (i2 - q5) + 3 * (q3 - i4)
|
||||
const uint32_t i2_m_q5_q3_m_i4 = __QSUB16(i2_q3, q5_i4); // 1: Rn[31:16]-Rm[31:16]:Rn[15:0]-Rm[15:0]
|
||||
const uint32_t d_i1 = __SMUADX(i2_m_q5_q3_m_i4, k_3_1); // 1: Rm[15:0]*Rs[31:16]+Rm[31:16]*Rs[15:0]
|
||||
|
||||
// D_Q1 = (i5 + q2) - 3 * (q4 + i3)
|
||||
const uint32_t q4_p_i3_i5_p_q2 = __QADD16(q4_i5, i3_q2); // 1: Rn[31:16]+Rm[31:16]:Rn[15:0]+Rm[15:0]
|
||||
const uint32_t d_q1 = __SMUSD(k_3_1, q4_p_i3_i5_p_q2); // 1: Rm[15:0]*Rs[15:0]–Rm[31:16]*Rs[31:16]
|
||||
const uint32_t d_q1_i1 = __PKHBT(d_i1, d_q1, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
|
||||
*(dst_p++) = d_q0_i0; // 3
|
||||
*(dst_p++) = d_q1_i1;
|
||||
|
||||
q1_i0 = q5_i4;
|
||||
q0_i1 = q4_i5;
|
||||
}
|
||||
_q1_i0 = q1_i0;
|
||||
_q0_i1 = q0_i1;
|
||||
|
||||
return { dst.p, src.count / 2, src.sampling_rate / 2 };
|
||||
}
|
||||
|
||||
buffer_c16_t DecimateBy2CIC3::execute(
|
||||
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<taps_count; j+=4) {
|
||||
t += z[j+0] * taps[j+0];
|
||||
t += z[j+1] * taps[j+1];
|
||||
t += z[j+2] * taps[j+2];
|
||||
t += z[j+3] * taps[j+3];
|
||||
|
||||
z[j+0] = z[j+0+2];
|
||||
z[j+1] = z[j+1+2];
|
||||
z[j+2] = z[j+2+2];
|
||||
z[j+3] = z[j+3+2];
|
||||
}
|
||||
*(dst_p++) = t / 65536;
|
||||
}
|
||||
|
||||
return { dst.p, src.count / 2, src.sampling_rate / 2 };
|
||||
}
|
||||
|
||||
buffer_c16_t FIRAndDecimateComplex::execute(
|
||||
buffer_c16_t src,
|
||||
buffer_c16_t dst
|
||||
) {
|
||||
/* int16_t input (sample count "n" must be multiple of decimation_factor)
|
||||
* -> int16_t output, decimated by decimation_factor.
|
||||
* taps are normalized to 1 << 16 == 1.0.
|
||||
*/
|
||||
const auto output_sampling_rate = src.sampling_rate / decimation_factor_;
|
||||
const size_t output_samples = src.count / decimation_factor_;
|
||||
|
||||
sample_t* dst_p = dst.p;
|
||||
const buffer_c16_t result { dst.p, output_samples, output_sampling_rate };
|
||||
|
||||
const sample_t* src_p = src.p;
|
||||
size_t outer_count = output_samples;
|
||||
while(outer_count > 0) {
|
||||
/* Put new samples into delay buffer */
|
||||
auto z_new_p = &samples_[taps_count_ - decimation_factor_];
|
||||
for(size_t i=0; i<decimation_factor_; i++) {
|
||||
*__SIMD32(z_new_p)++ = *__SIMD32(src_p)++;
|
||||
}
|
||||
|
||||
size_t loop_count = taps_count_ / 8;
|
||||
auto t_p = &taps_reversed_[0];
|
||||
auto z_p = &samples_[0];
|
||||
|
||||
int64_t t_real = 0;
|
||||
int64_t t_imag = 0;
|
||||
|
||||
while(loop_count > 0) {
|
||||
const auto tap0 = *__SIMD32(t_p)++;
|
||||
const auto sample0 = *__SIMD32(z_p)++;
|
||||
const auto tap1 = *__SIMD32(t_p)++;
|
||||
const auto sample1 = *__SIMD32(z_p)++;
|
||||
t_real = __SMLSLD(sample0, tap0, t_real);
|
||||
t_imag = __SMLALDX(sample0, tap0, t_imag);
|
||||
t_real = __SMLSLD(sample1, tap1, t_real);
|
||||
t_imag = __SMLALDX(sample1, tap1, t_imag);
|
||||
|
||||
const auto tap2 = *__SIMD32(t_p)++;
|
||||
const auto sample2 = *__SIMD32(z_p)++;
|
||||
const auto tap3 = *__SIMD32(t_p)++;
|
||||
const auto sample3 = *__SIMD32(z_p)++;
|
||||
t_real = __SMLSLD(sample2, tap2, t_real);
|
||||
t_imag = __SMLALDX(sample2, tap2, t_imag);
|
||||
t_real = __SMLSLD(sample3, tap3, t_real);
|
||||
t_imag = __SMLALDX(sample3, tap3, t_imag);
|
||||
|
||||
const auto tap4 = *__SIMD32(t_p)++;
|
||||
const auto sample4 = *__SIMD32(z_p)++;
|
||||
const auto tap5 = *__SIMD32(t_p)++;
|
||||
const auto sample5 = *__SIMD32(z_p)++;
|
||||
t_real = __SMLSLD(sample4, tap4, t_real);
|
||||
t_imag = __SMLALDX(sample4, tap4, t_imag);
|
||||
t_real = __SMLSLD(sample5, tap5, t_real);
|
||||
t_imag = __SMLALDX(sample5, tap5, t_imag);
|
||||
|
||||
const auto tap6 = *__SIMD32(t_p)++;
|
||||
const auto sample6 = *__SIMD32(z_p)++;
|
||||
const auto tap7 = *__SIMD32(t_p)++;
|
||||
const auto sample7 = *__SIMD32(z_p)++;
|
||||
t_real = __SMLSLD(sample6, tap6, t_real);
|
||||
t_imag = __SMLALDX(sample6, tap6, t_imag);
|
||||
t_real = __SMLSLD(sample7, tap7, t_real);
|
||||
t_imag = __SMLALDX(sample7, tap7, t_imag);
|
||||
|
||||
loop_count--;
|
||||
}
|
||||
|
||||
/* TODO: Re-evaluate whether saturation is performed, normalization,
|
||||
* all that jazz.
|
||||
*/
|
||||
const int32_t r = t_real >> 16;
|
||||
const int32_t i = t_imag >> 16;
|
||||
const int32_t r_sat = __SSAT(r, 16);
|
||||
const int32_t i_sat = __SSAT(i, 16);
|
||||
*__SIMD32(dst_p)++ = __PKHBT(
|
||||
r_sat,
|
||||
i_sat,
|
||||
16
|
||||
);
|
||||
|
||||
/* Shift sample buffer left/down by decimation factor. */
|
||||
const size_t unroll_factor = 4;
|
||||
size_t shift_count = (taps_count_ - decimation_factor_) / unroll_factor;
|
||||
|
||||
sample_t* t = &samples_[0];
|
||||
const sample_t* s = &samples_[decimation_factor_];
|
||||
|
||||
while(shift_count > 0) {
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
shift_count--;
|
||||
}
|
||||
|
||||
shift_count = (taps_count_ - decimation_factor_) % unroll_factor;
|
||||
while(shift_count > 0) {
|
||||
*(t++) = *(s++);
|
||||
shift_count--;
|
||||
}
|
||||
|
||||
outer_count--;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
buffer_s16_t DecimateBy2CIC4Real::execute(
|
||||
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 */
|
242
firmware/baseband-tx/dsp_decimate.hpp
Normal file
242
firmware/baseband-tx/dsp_decimate.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <algorithm>
|
||||
|
||||
#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<int16_t, taps_count>& taps
|
||||
) : taps(taps)
|
||||
{
|
||||
}
|
||||
|
||||
buffer_s16_t execute(
|
||||
buffer_s16_t src,
|
||||
buffer_s16_t dst
|
||||
);
|
||||
|
||||
private:
|
||||
std::array<int16_t, taps_count + 2> z;
|
||||
const std::array<int16_t, taps_count>& 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<typename T>
|
||||
void configure(
|
||||
const T& taps,
|
||||
const size_t decimation_factor
|
||||
) {
|
||||
samples_ = std::make_unique<samples_t>(taps.size());
|
||||
taps_reversed_ = std::make_unique<taps_t>(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_t> samples_;
|
||||
std::unique_ptr<taps_t> 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<size_t N>
|
||||
class FIRAndDecimateBy2Complex {
|
||||
public:
|
||||
FIR64AndDecimateBy2Complex(
|
||||
const std::array<int16_t, N>& 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<complex16_t, N> z;
|
||||
const std::array<int16_t, N>& taps;
|
||||
|
||||
complex<int16_t> process_one(const size_t start_offset) {
|
||||
const auto split = &z[start_offset];
|
||||
const auto end = &z[z.size()];
|
||||
auto tap = &taps[0];
|
||||
|
||||
complex<int32_t> 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__*/
|
111
firmware/baseband-tx/dsp_demodulate.cpp
Normal file
111
firmware/baseband-tx/dsp_demodulate.cpp
Normal file
@ -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 <hal.h>
|
||||
|
||||
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<float>(t.imag()) / static_cast<float>(t.real());
|
||||
return 16384.0f * x;
|
||||
}
|
||||
*/
|
||||
static inline float angle_approx_0deg27(const complex32_t t) {
|
||||
const auto x = static_cast<float>(t.imag()) / static_cast<float>(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 };
|
||||
}
|
||||
|
||||
}
|
||||
}
|
70
firmware/baseband-tx/dsp_demodulate.hpp
Normal file
70
firmware/baseband-tx/dsp_demodulate.hpp
Normal file
@ -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<float>(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<float>(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__*/
|
22
firmware/baseband-tx/dsp_fir_taps.cpp
Normal file
22
firmware/baseband-tx/dsp_fir_taps.cpp
Normal file
@ -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"
|
205
firmware/baseband-tx/dsp_fir_taps.hpp
Normal file
205
firmware/baseband-tx/dsp_fir_taps.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "complex.hpp"
|
||||
|
||||
template<size_t N>
|
||||
struct fir_taps_real {
|
||||
float pass_frequency_normalized;
|
||||
float stop_frequency_normalized;
|
||||
std::array<int16_t, N> 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__*/
|
75
firmware/baseband-tx/dsp_iir.hpp
Normal file
75
firmware/baseband-tx/dsp_iir.hpp
Normal file
@ -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 <array>
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
struct iir_biquad_config_t {
|
||||
const std::array<float, 3> b;
|
||||
const std::array<float, 3> 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<buffer_out.count; i++) {
|
||||
buffer_out.p[i] = execute_sample(buffer_in.p[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void execute_in_place(buffer_s16_t buffer) {
|
||||
execute(buffer, buffer);
|
||||
}
|
||||
|
||||
private:
|
||||
const iir_biquad_config_t config;
|
||||
std::array<float, 3> x { { 0.0f, 0.0f, 0.0f } };
|
||||
std::array<float, 3> 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__*/
|
37
firmware/baseband-tx/dsp_iir_config.hpp
Normal file
37
firmware/baseband-tx/dsp_iir_config.hpp
Normal file
@ -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__*/
|
45
firmware/baseband-tx/dsp_squelch.cpp
Normal file
45
firmware/baseband-tx/dsp_squelch.cpp
Normal file
@ -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 <cstdint>
|
||||
#include <array>
|
||||
|
||||
bool FMSquelch::execute(buffer_s16_t audio) {
|
||||
// TODO: No hard-coded array size.
|
||||
std::array<int16_t, N> 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));
|
||||
}
|
45
firmware/baseband-tx/dsp_squelch.hpp
Normal file
45
firmware/baseband-tx/dsp_squelch.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
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__*/
|
30
firmware/baseband-tx/event_m4.cpp
Normal file
30
firmware/baseband-tx/event_m4.cpp
Normal file
@ -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;
|
||||
}
|
46
firmware/baseband-tx/event_m4.hpp
Normal file
46
firmware/baseband-tx/event_m4.hpp
Normal file
@ -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__*/
|
152
firmware/baseband-tx/fxpt_atan2.cpp
Normal file
152
firmware/baseband-tx/fxpt_atan2.cpp
Normal file
@ -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 <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/**
|
||||
* Convert floating point to Q15 (1.0.15 fixed point) format.
|
||||
*
|
||||
* @param d floating-point value within range -1 to (1 - (2**-15)), inclusive
|
||||
* @return Q15 value representing d; same range
|
||||
*/
|
||||
/*
|
||||
static inline int16_t q15_from_double(const double d) {
|
||||
return lrint(d * 32768);
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Negative absolute value. Used to avoid undefined behavior for most negative
|
||||
* integer (see C99 standard 7.20.6.1.2 and footnote 265 for the description of
|
||||
* abs/labs/llabs behavior).
|
||||
*
|
||||
* @param i 16-bit signed integer
|
||||
* @return negative absolute value of i; defined for all values of i
|
||||
*/
|
||||
/*
|
||||
static inline int16_t s16_nabs(const int16_t j) {
|
||||
#if (((int16_t)-1) >> 1) == ((int16_t)-1)
|
||||
// signed right shift sign-extends (arithmetic)
|
||||
const int16_t negSign = ~(j >> 15); // splat sign bit into all 16 and complement
|
||||
// if j is positive (negSign is -1), xor will invert j and sub will add 1
|
||||
// otherwise j is unchanged
|
||||
return (j ^ negSign) - negSign;
|
||||
#else
|
||||
return (j < 0 ? j : -j);
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Q15 (1.0.15 fixed point) multiplication. Various common rounding modes are in
|
||||
* the function definition for reference (and preference).
|
||||
*
|
||||
* @param j 16-bit signed integer representing -1 to (1 - (2**-15)), inclusive
|
||||
* @param k same format as j
|
||||
* @return product of j and k, in same format
|
||||
*/
|
||||
static inline int16_t q15_mul(const int16_t j, const int16_t k) {
|
||||
const int32_t intermediate = j * k;
|
||||
#if 0 // don't round
|
||||
return intermediate >> 15;
|
||||
#elif 0 // biased rounding
|
||||
return (intermediate + 0x4000) >> 15;
|
||||
#else // unbiased rounding
|
||||
return (intermediate + ((intermediate & 0x7FFF) == 0x4000 ? 0 : 0x4000)) >> 15;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Q15 (1.0.15 fixed point) division (non-saturating). Be careful when using
|
||||
* this function, as it does not behave well when the result is out-of-range.
|
||||
*
|
||||
* Value is not defined if numerator is greater than or equal to denominator.
|
||||
*
|
||||
* @param numer 16-bit signed integer representing -1 to (1 - (2**-15))
|
||||
* @param denom same format as numer; must be greater than numerator
|
||||
* @return numer / denom in same format as numer and denom
|
||||
*/
|
||||
static inline int16_t q15_div(const int16_t numer, const int16_t denom) {
|
||||
return (static_cast<int32_t>(numer) << 15) / denom;
|
||||
}
|
||||
|
||||
/**
|
||||
* 16-bit fixed point four-quadrant arctangent. Given some Cartesian vector
|
||||
* (x, y), find the angle subtended by the vector and the positive x-axis.
|
||||
*
|
||||
* The value returned is in units of 1/65536ths of one turn. This allows the use
|
||||
* of the full 16-bit unsigned range to represent a turn. e.g. 0x0000 is 0
|
||||
* radians, 0x8000 is pi radians, and 0xFFFF is (65535 / 32768) * pi radians.
|
||||
*
|
||||
* Because the magnitude of the input vector does not change the angle it
|
||||
* represents, the inputs can be in any signed 16-bit fixed-point format.
|
||||
*
|
||||
* @param y y-coordinate in signed 16-bit
|
||||
* @param x x-coordinate in signed 16-bit
|
||||
* @return angle in (val / 32768) * pi radian increments from 0x0000 to 0xFFFF
|
||||
*/
|
||||
|
||||
static inline int16_t nabs(const int16_t j) {
|
||||
//return -abs(x);
|
||||
return (j < 0 ? j : -j);
|
||||
}
|
||||
|
||||
int16_t fxpt_atan2(const int16_t y, const int16_t x) {
|
||||
static const int16_t k1 = 2847;
|
||||
static const int16_t k2 = 11039;
|
||||
if (x == y) { // x/y or y/x would return -1 since 1 isn't representable
|
||||
if (y > 0) { // 1/8
|
||||
return 8192;
|
||||
} else if (y < 0) { // 5/8
|
||||
return 40960;
|
||||
} else { // x = y = 0
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
const int16_t nabs_y = nabs(y);
|
||||
const int16_t nabs_x = nabs(x);
|
||||
if (nabs_x < nabs_y) { // octants 1, 4, 5, 8
|
||||
const int16_t y_over_x = q15_div(y, x);
|
||||
const int16_t correction = q15_mul(k1, nabs(y_over_x));
|
||||
const int16_t unrotated = q15_mul(k2 + correction, y_over_x);
|
||||
if (x > 0) { // octants 1, 8
|
||||
return unrotated;
|
||||
} else { // octants 4, 5
|
||||
return 32768 + unrotated;
|
||||
}
|
||||
} else { // octants 2, 3, 6, 7
|
||||
const int16_t x_over_y = q15_div(x, y);
|
||||
const int16_t correction = q15_mul(k1, nabs(x_over_y));
|
||||
const int16_t unrotated = q15_mul(k2 + correction, x_over_y);
|
||||
if (y > 0) { // octants 2, 3
|
||||
return 16384 - unrotated;
|
||||
} else { // octants 6, 7
|
||||
return 49152 - unrotated;
|
||||
}
|
||||
}
|
||||
}
|
29
firmware/baseband-tx/fxpt_atan2.hpp
Normal file
29
firmware/baseband-tx/fxpt_atan2.hpp
Normal file
@ -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 <cstdint>
|
||||
|
||||
int16_t fxpt_atan2(const int16_t y, const int16_t x);
|
||||
|
||||
#endif/*__FXPT_ATAN2_H__*/
|
225
firmware/baseband-tx/gpdma_lli.hpp
Normal file
225
firmware/baseband-tx/gpdma_lli.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "gpdma.hpp"
|
||||
|
||||
namespace lpc43xx {
|
||||
namespace gpdma {
|
||||
namespace lli {
|
||||
|
||||
enum class ChainType : uint8_t {
|
||||
Loop = 0,
|
||||
OneShot = 1,
|
||||
};
|
||||
|
||||
enum class Interrupt : uint8_t {
|
||||
All = 0,
|
||||
Last = 1,
|
||||
};
|
||||
|
||||
struct ChainConfig {
|
||||
ChainType type;
|
||||
size_t length;
|
||||
Interrupt interrupt;
|
||||
};
|
||||
|
||||
enum class BurstSize : uint8_t {
|
||||
Transfer1 = 0,
|
||||
Transfer4 = 1,
|
||||
Transfer8 = 2,
|
||||
Transfer16 = 3,
|
||||
Transfer32 = 4,
|
||||
Transfer64 = 5,
|
||||
Transfer128 = 6,
|
||||
Transfer256 = 7,
|
||||
};
|
||||
|
||||
enum class TransferWidth : uint8_t {
|
||||
Byte = 0,
|
||||
HalfWord = 1,
|
||||
Word = 2,
|
||||
};
|
||||
|
||||
enum class Increment : uint8_t {
|
||||
No = 0,
|
||||
Yes = 1,
|
||||
};
|
||||
|
||||
using PeripheralIndex = uint8_t;
|
||||
|
||||
struct Endpoint {
|
||||
PeripheralIndex peripheral;
|
||||
BurstSize burst_size;
|
||||
TransferWidth transfer_size;
|
||||
Increment increment;
|
||||
};
|
||||
|
||||
struct ChannelConfig {
|
||||
ChainConfig chain;
|
||||
FlowControl flow_control;
|
||||
Endpoint source;
|
||||
Endpoint destination;
|
||||
|
||||
constexpr gpdma::channel::Control control(
|
||||
const size_t transfer_size,
|
||||
const bool last
|
||||
) {
|
||||
return {
|
||||
.transfersize = transfer_size,
|
||||
.sbsize = toUType(source.burst_size),
|
||||
.dbsize = toUType(destination.burst_size),
|
||||
.swidth = toUType(source.transfer_size),
|
||||
.dwidth = toUType(destination.transfer_size),
|
||||
.s = source_endpoint_type(flow_control),
|
||||
.d = destination_endpoint_type(flow_control),
|
||||
.si = toUType(source.increment),
|
||||
.di = toUType(destination.increment),
|
||||
.prot1 = 0,
|
||||
.prot2 = 0,
|
||||
.prot3 = 0,
|
||||
.i = ((chain.interrupt == Interrupt::All) || last) ? 1U : 0U,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Config config() {
|
||||
return {
|
||||
.e = 0,
|
||||
.srcperipheral = source.peripheral,
|
||||
.destperipheral = destination.peripheral,
|
||||
.flowcntrl = flow_control,
|
||||
.ie = 1,
|
||||
.itc = 1,
|
||||
.l = 0,
|
||||
.a = 0,
|
||||
.h = 0,
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
constexpr ChannelConfig channel_config_baseband_tx {
|
||||
{ ChainType::Loop, 4, Interrupt::All },
|
||||
gpdma::FlowControl::MemoryToPeripheral_DMAControl,
|
||||
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes },
|
||||
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No },
|
||||
};
|
||||
|
||||
constexpr ChannelConfig channel_config_baseband_rx {
|
||||
{ ChainType::Loop, 4, Interrupt::All },
|
||||
gpdma::FlowControl::PeripheralToMemory_DMAControl,
|
||||
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::No },
|
||||
{ 0x00, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes },
|
||||
};
|
||||
|
||||
constexpr ChannelConfig channel_config_audio_tx {
|
||||
{ ChainType::Loop, 4, Interrupt::All },
|
||||
gpdma::FlowControl::MemoryToPeripheral_DMAControl,
|
||||
{ 0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes },
|
||||
{ 0x0a, BurstSize::Transfer32, TransferWidth::Word, Increment::No },
|
||||
};
|
||||
|
||||
constexpr ChannelConfig channel_config_audio_rx {
|
||||
{ ChainType::Loop, 4, Interrupt::All },
|
||||
gpdma::FlowControl::PeripheralToMemory_DMAControl,
|
||||
{ 0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::No },
|
||||
{ 0x09, BurstSize::Transfer32, TransferWidth::Word, Increment::Yes },
|
||||
};
|
||||
|
||||
constexpr ChannelConfig channel_config_rssi {
|
||||
{ ChainType::Loop, 4, Interrupt::All },
|
||||
gpdma::FlowControl::PeripheralToMemory_DMAControl,
|
||||
{ 0x0e, BurstSize::Transfer1, TransferWidth::Byte, Increment::No },
|
||||
{ 0x0e, BurstSize::Transfer1, TransferWidth::Word, Increment::Yes },
|
||||
};
|
||||
|
||||
class Chain {
|
||||
public:
|
||||
using chain_t = std::vector<gpdma::channel::LLI>;
|
||||
using chain_p = std::unique_ptr<chain_t>;
|
||||
|
||||
Chain(const ChannelConfig& cc) :
|
||||
chain(std::make_unique<chain_t>(cc.chain.length))
|
||||
{
|
||||
set_lli_sequential(cc.chain_type);
|
||||
set_source_address()...
|
||||
}
|
||||
|
||||
private:
|
||||
chain_p chain;
|
||||
|
||||
void set_source_peripheral(void* const address) {
|
||||
set_source_address(address, 0);
|
||||
}
|
||||
|
||||
void set_destination_peripheral(void* const address) {
|
||||
set_destination_address(address, 0);
|
||||
}
|
||||
|
||||
void set_source_address(void* const address, const size_t increment) {
|
||||
size_t offset = 0;
|
||||
for(auto& item : *chain) {
|
||||
item.srcaddr = (uint32_t)address + offset;
|
||||
offset += increment;
|
||||
}
|
||||
}
|
||||
|
||||
void set_destination_address(void* const address, const size_t increment) {
|
||||
size_t offset = 0;
|
||||
for(auto& item : *chain) {
|
||||
item.destaddr = (uint32_t)address + offset;
|
||||
offset += increment;
|
||||
}
|
||||
}
|
||||
|
||||
void set_control(const gpdma::channel::Control control) {
|
||||
for(auto& item : *chain) {
|
||||
item.control = control;
|
||||
}
|
||||
}
|
||||
|
||||
void set_lli_sequential(ChainType chain_type) {
|
||||
for(auto& item : *chain) {
|
||||
item.lli = lli_pointer(&item + 1);
|
||||
}
|
||||
if( chain_type == ChainType::Loop ) {
|
||||
chain[chain->size() - 1].lli = lli_pointer(&chain[0]);
|
||||
} else {
|
||||
chain[chain->size() - 1].lli = lli_pointer(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
gpdma::channel::LLIPointer lli_pointer(const void* lli) {
|
||||
return {
|
||||
.lm = 0,
|
||||
.r = 0,
|
||||
.lli = reinterpret_cast<uint32_t>(lli),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace lli */
|
||||
} /* namespace gpdma */
|
||||
} /* namespace lpc43xx */
|
313
firmware/baseband-tx/halconf.h
Executable file
313
firmware/baseband-tx/halconf.h
Executable file
@ -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_ */
|
||||
|
||||
/** @} */
|
54
firmware/baseband-tx/irq_ipc_m4.cpp
Normal file
54
firmware/baseband-tx/irq_ipc_m4.cpp
Normal file
@ -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();
|
||||
}
|
||||
|
||||
}
|
28
firmware/baseband-tx/irq_ipc_m4.hpp
Normal file
28
firmware/baseband-tx/irq_ipc_m4.hpp
Normal file
@ -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__*/
|
69
firmware/baseband-tx/linear_resampler.hpp
Normal file
69
firmware/baseband-tx/linear_resampler.hpp
Normal file
@ -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<typename InterpolatedSampleHandler>
|
||||
void operator()(
|
||||
const float sample,
|
||||
InterpolatedSampleHandler interpolated_sample_handler
|
||||
) {
|
||||
const float sample_delta = sample - last_sample;
|
||||
while( phase < 1.0f ) {
|
||||
const float interpolated_value = last_sample + phase * sample_delta;
|
||||
interpolated_sample_handler(interpolated_value);
|
||||
phase += phase_increment;
|
||||
}
|
||||
last_sample = sample;
|
||||
phase -= 1.0f;
|
||||
}
|
||||
|
||||
void advance(const float fraction) {
|
||||
phase += (fraction * phase_increment);
|
||||
}
|
||||
|
||||
private:
|
||||
float last_sample { 0.0f };
|
||||
float phase { 0.0f };
|
||||
float phase_increment { 0.0f };
|
||||
|
||||
static constexpr float calculate_increment(const float input_rate, const float output_rate) {
|
||||
return input_rate / output_rate;
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace interpolation */
|
||||
} /* namespace dsp */
|
||||
|
||||
#endif/*__LINEAR_RESAMPLER_H__*/
|
561
firmware/baseband-tx/main.cpp
Executable file
561
firmware/baseband-tx/main.cpp
Executable file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <bitset>
|
||||
#include <math.h>
|
||||
|
||||
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<ThreadBase*>(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<buffer.count; i++) {
|
||||
|
||||
//Sample generation 2.28M/10=228kHz
|
||||
if(s >= 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<FILTER_SIZE; j++) {
|
||||
val = (*src++);
|
||||
if (cur_output) val = -val;
|
||||
sample_buffer[idx++] += val;
|
||||
if (idx >= 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<buffer.count; i++) {
|
||||
|
||||
//Sample generation 2.28M/10 = 228kHz
|
||||
if (s >= 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<uint8_t, Message::MAX_SIZE> 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<baseband::sample_t, 8192>();
|
||||
|
||||
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<const BasebandConfigurationMessage*>(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;
|
||||
}
|
1003
firmware/baseband-tx/main.cpp.orig
Executable file
1003
firmware/baseband-tx/main.cpp.orig
Executable file
File diff suppressed because it is too large
Load Diff
89
firmware/baseband-tx/matched_filter.cpp
Normal file
89
firmware/baseband-tx/matched_filter.cpp
Normal file
@ -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<taps_count_; n++) {
|
||||
const auto sample = samples_[n];
|
||||
const auto tap = taps_reversed_[n];
|
||||
|
||||
sr_tr += sample.real() * tap.real();
|
||||
si_ti += sample.imag() * tap.imag();
|
||||
si_tr += sample.imag() * tap.real();
|
||||
sr_ti += sample.real() * tap.imag();
|
||||
}
|
||||
|
||||
// N: complex multiple of samples and taps (conjugate, tap.i negated).
|
||||
// P: complex multiply of samples and taps.
|
||||
const auto r_n = sr_tr + si_ti;
|
||||
const auto r_p = sr_tr - si_ti;
|
||||
const auto i_n = si_tr - sr_ti;
|
||||
const auto i_p = si_tr + sr_ti;
|
||||
|
||||
const auto mag_n = std::sqrt(r_n * r_n + i_n * i_n);
|
||||
const auto mag_p = std::sqrt(r_p * r_p + i_p * i_p);
|
||||
const auto diff = mag_p - mag_n;
|
||||
output = diff;
|
||||
|
||||
shift_by_decimation_factor();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void MatchedFilter::shift_by_decimation_factor() {
|
||||
const sample_t* s = &samples_[decimation_factor_];
|
||||
sample_t* t = &samples_[0];
|
||||
|
||||
const size_t unroll_factor = 4;
|
||||
size_t shift_count = (taps_count_ - decimation_factor_) / unroll_factor;
|
||||
while(shift_count > 0) {
|
||||
*t++ = *s++;
|
||||
*t++ = *s++;
|
||||
*t++ = *s++;
|
||||
*t++ = *s++;
|
||||
shift_count--;
|
||||
}
|
||||
|
||||
shift_count = (taps_count_ - decimation_factor_) % unroll_factor;
|
||||
while(shift_count > 0) {
|
||||
*t++ = *s++;
|
||||
shift_count--;
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace matched_filter */
|
||||
} /* namespace dsp */
|
101
firmware/baseband-tx/matched_filter.hpp
Normal file
101
firmware/baseband-tx/matched_filter.hpp
Normal file
@ -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 <cstddef>
|
||||
|
||||
#include <complex>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
namespace dsp {
|
||||
namespace matched_filter {
|
||||
|
||||
// This filter contains "magic" (optimizations) that expect the taps to
|
||||
// combine a low-pass filter with a complex sinusoid that performs shifting of
|
||||
// the input signal to 0Hz/DC. This also means that the taps length must be
|
||||
// a multiple of the complex sinusoid period.
|
||||
|
||||
class MatchedFilter {
|
||||
public:
|
||||
using sample_t = std::complex<float>;
|
||||
using tap_t = std::complex<float>;
|
||||
|
||||
using taps_t = tap_t[];
|
||||
|
||||
template<class T>
|
||||
MatchedFilter(
|
||||
const T& taps,
|
||||
size_t decimation_factor = 1
|
||||
) {
|
||||
configure(taps, decimation_factor);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void configure(
|
||||
const T& taps,
|
||||
size_t decimation_factor
|
||||
) {
|
||||
samples_ = std::make_unique<samples_t>(taps.size());
|
||||
taps_reversed_ = std::make_unique<taps_t>(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_t> samples_;
|
||||
std::unique_ptr<taps_t> taps_reversed_;
|
||||
size_t taps_count_ { 0 };
|
||||
size_t decimation_factor_ { 1 };
|
||||
size_t decimation_phase { 0 };
|
||||
float output;
|
||||
|
||||
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__*/
|
47
firmware/baseband-tx/mcuconf.h
Executable file
47
firmware/baseband-tx/mcuconf.h
Executable file
@ -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
|
1
firmware/baseband-tx/name
Normal file
1
firmware/baseband-tx/name
Normal file
@ -0,0 +1 @@
|
||||
Second module
|
22
firmware/baseband-tx/packet_builder.cpp
Normal file
22
firmware/baseband-tx/packet_builder.cpp
Normal file
@ -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"
|
120
firmware/baseband-tx/packet_builder.hpp
Normal file
120
firmware/baseband-tx/packet_builder.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
#include <bitset>
|
||||
#include <functional>
|
||||
|
||||
#include "bit_pattern.hpp"
|
||||
|
||||
template<typename PreambleMatcher, typename UnstuffMatcher, typename EndMatcher>
|
||||
class PacketBuilder {
|
||||
public:
|
||||
using PayloadType = std::bitset<1024>;
|
||||
using PayloadHandlerFunc = std::function<void(const PayloadType& payload, const size_t bits_received)>;
|
||||
|
||||
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__*/
|
92
firmware/baseband-tx/proc_fsk_lcr.cpp
Normal file
92
firmware/baseband-tx/proc_fsk_lcr.cpp
Normal file
@ -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 <cstdint>
|
||||
|
||||
void LCRFSKProcessor::execute(buffer_c8_t buffer) {
|
||||
|
||||
for (size_t i = 0; i<buffer.count; i++) {
|
||||
|
||||
//Sample generation 2.28M/10 = 228kHz
|
||||
if (s >= 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};
|
||||
}
|
||||
}
|
44
firmware/baseband-tx/proc_fsk_lcr.hpp
Normal file
44
firmware/baseband-tx/proc_fsk_lcr.hpp
Normal file
@ -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
|
105
firmware/baseband-tx/proc_jammer.cpp
Normal file
105
firmware/baseband-tx/proc_jammer.cpp
Normal file
@ -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 <cstdint>
|
||||
|
||||
#define POLY_MASK_32 0xB4BCD35C
|
||||
|
||||
|
||||
void JammerProcessor::execute(buffer_c8_t buffer) {
|
||||
for (size_t i = 0; i<buffer.count; i++) {
|
||||
|
||||
/*if (s > 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};
|
||||
}
|
||||
};
|
44
firmware/baseband-tx/proc_jammer.hpp
Normal file
44
firmware/baseband-tx/proc_jammer.hpp
Normal file
@ -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
|
74
firmware/baseband-tx/rssi.cpp
Normal file
74
firmware/baseband-tx/rssi.cpp
Normal file
@ -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 <cstdint>
|
||||
|
||||
#include "adc.hpp"
|
||||
#include "rssi_dma.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "hal.h"
|
||||
using namespace lpc43xx;
|
||||
|
||||
#include "hackrf_hal.hpp"
|
||||
using namespace hackrf::one;
|
||||
|
||||
#include "portapack_adc.hpp"
|
||||
|
||||
namespace rf {
|
||||
namespace rssi {
|
||||
|
||||
constexpr uint8_t adc1_sel = (1 << portapack::adc1_rssi_input);
|
||||
const auto adc1_interrupt_mask = flp2(adc1_sel);
|
||||
|
||||
constexpr adc::CR adc1_cr {
|
||||
.sel = adc1_sel,
|
||||
.clkdiv = 49, /* 400kHz sample rate, 2.5us/sample @ 200MHz PCLK */
|
||||
.resolution = 9, /* Ten clocks */
|
||||
.edge = 0,
|
||||
};
|
||||
constexpr adc::Config adc1_config {
|
||||
.cr = adc1_cr,
|
||||
};
|
||||
|
||||
void init() {
|
||||
adc1.clock_enable();
|
||||
adc1.interrupts_disable();
|
||||
adc1.power_up(adc1_config);
|
||||
adc1.interrupts_enable(adc1_interrupt_mask);
|
||||
|
||||
dma::init();
|
||||
}
|
||||
|
||||
void start() {
|
||||
dma::enable();
|
||||
adc1.start_burst();
|
||||
}
|
||||
|
||||
void stop() {
|
||||
dma::disable();
|
||||
adc1.stop_burst();
|
||||
}
|
||||
|
||||
} /* namespace rssi */
|
||||
} /* namespace rf */
|
43
firmware/baseband-tx/rssi.hpp
Normal file
43
firmware/baseband-tx/rssi.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include "buffer.hpp"
|
||||
|
||||
namespace rf {
|
||||
namespace rssi {
|
||||
|
||||
using sample_t = uint8_t;
|
||||
using buffer_t = buffer_t<sample_t>;
|
||||
|
||||
void init();
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
} /* namespace rssi */
|
||||
} /* namespace rf */
|
||||
|
||||
#endif/*__RSSI_H__*/
|
182
firmware/baseband-tx/rssi_dma.cpp
Normal file
182
firmware/baseband-tx/rssi_dma.cpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#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<uint32_t>(lli),
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Control control(const size_t number_of_transfers) {
|
||||
return {
|
||||
.transfersize = number_of_transfers,
|
||||
.sbsize = 0, /* Burst size: 1 transfer */
|
||||
.dbsize = 0, /* Burst size: 1 transfer */
|
||||
.swidth = 0, /* Source transfer width: byte (8 bits) */
|
||||
.dwidth = 2, /* Destination transfer width: word (32 bits) */
|
||||
.s = gpdma_ahb_master_peripheral,
|
||||
.d = gpdma_ahb_master_memory,
|
||||
.si = 0,
|
||||
.di = 1,
|
||||
.prot1 = 0,
|
||||
.prot2 = 0,
|
||||
.prot3 = 0,
|
||||
.i = 1,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Config config() {
|
||||
return {
|
||||
.e = 0,
|
||||
.srcperipheral = gpdma_src_peripheral,
|
||||
.destperipheral = gpdma_dest_peripheral,
|
||||
.flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl,
|
||||
.ie = 1,
|
||||
.itc = 1,
|
||||
.l = 0,
|
||||
.a = 0,
|
||||
.h = 0,
|
||||
};
|
||||
}
|
||||
|
||||
struct buffers_config_t {
|
||||
size_t count;
|
||||
size_t items_per_buffer;
|
||||
};
|
||||
|
||||
static buffers_config_t buffers_config;
|
||||
|
||||
static sample_t *samples { nullptr };
|
||||
static gpdma::channel::LLI *lli { nullptr };
|
||||
|
||||
static 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<uint32_t>(&LPC_ADC1->DR[portapack::adc1_rssi_input]) + 1;
|
||||
const auto control_value = control(gpdma::buffer_words(buffers_config.items_per_buffer, 1));
|
||||
|
||||
samples = new sample_t[buffers_config.count * buffers_config.items_per_buffer];
|
||||
lli = new gpdma::channel::LLI[buffers_config.count];
|
||||
|
||||
for(size_t i=0; i<buffers_config.count; i++) {
|
||||
const auto memory = reinterpret_cast<uint32_t>(&samples[i * buffers_config.items_per_buffer]);
|
||||
lli[i].srcaddr = peripheral;
|
||||
lli[i].destaddr = memory;
|
||||
lli[i].lli = lli_pointer(&lli[(i + 1) % buffers_config.count]);
|
||||
lli[i].control = control_value;
|
||||
}
|
||||
}
|
||||
|
||||
void free() {
|
||||
delete samples;
|
||||
delete lli;
|
||||
}
|
||||
|
||||
void enable() {
|
||||
const auto gpdma_config = config();
|
||||
gpdma_channel.configure(lli[0], gpdma_config);
|
||||
|
||||
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<sample_t*>(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 */
|
51
firmware/baseband-tx/rssi_dma.hpp
Normal file
51
firmware/baseband-tx/rssi_dma.hpp
Normal file
@ -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 <cstddef>
|
||||
|
||||
#include "rssi.hpp"
|
||||
|
||||
namespace rf {
|
||||
namespace rssi {
|
||||
namespace dma {
|
||||
|
||||
using Handler = void (*)();
|
||||
|
||||
void init();
|
||||
|
||||
void allocate(size_t buffer_count, size_t items_per_buffer);
|
||||
void free();
|
||||
|
||||
void enable();
|
||||
bool is_enabled();
|
||||
|
||||
void disable();
|
||||
|
||||
rf::rssi::buffer_t wait_for_buffer();
|
||||
|
||||
} /* namespace dma */
|
||||
} /* namespace rssi */
|
||||
} /* namespace rf */
|
||||
|
||||
#endif/*__RSSI_DMA_H__*/
|
75
firmware/baseband-tx/rssi_stats_collector.hpp
Normal file
75
firmware/baseband-tx/rssi_stats_collector.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
class RSSIStatisticsCollector {
|
||||
public:
|
||||
template<typename Callback>
|
||||
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__*/
|
44
firmware/baseband-tx/symbol_coding.hpp
Normal file
44
firmware/baseband-tx/symbol_coding.hpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace symbol_coding {
|
||||
|
||||
class NRZIDecoder {
|
||||
public:
|
||||
uint_fast8_t operator()(const uint_fast8_t symbol) {
|
||||
const auto out = (~(symbol ^ last)) & 1;
|
||||
last = symbol;
|
||||
return out;
|
||||
}
|
||||
|
||||
private:
|
||||
uint_fast8_t last { 0 };
|
||||
};
|
||||
|
||||
} /* namespace symbol_coding */
|
||||
|
||||
#endif/*__SYMBOL_CODING_H__*/
|
129
firmware/baseband-tx/touch_dma.cpp
Normal file
129
firmware/baseband-tx/touch_dma.cpp
Normal file
@ -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 <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
#include "hal.h"
|
||||
#include "gpdma.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
#include "portapack_dma.hpp"
|
||||
#include "portapack_adc.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
namespace touch {
|
||||
namespace dma {
|
||||
|
||||
|
||||
/* TODO: SO MUCH REPEATED CODE FROM rssi_dma.cpp!!! */
|
||||
|
||||
static constexpr auto& gpdma_channel = gpdma::channels[portapack::adc0_gpdma_channel_number];
|
||||
|
||||
constexpr uint32_t gpdma_ahb_master_peripheral = 1;
|
||||
constexpr uint32_t gpdma_ahb_master_memory = 0;
|
||||
constexpr uint32_t gpdma_ahb_master_lli_fetch = 0;
|
||||
|
||||
constexpr uint32_t gpdma_src_peripheral = 0xd;
|
||||
constexpr uint32_t gpdma_dest_peripheral = 0xd;
|
||||
|
||||
constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) {
|
||||
return {
|
||||
.lm = gpdma_ahb_master_lli_fetch,
|
||||
.r = 0,
|
||||
.lli = reinterpret_cast<uint32_t>(lli),
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Control control(const size_t number_of_transfers) {
|
||||
return {
|
||||
.transfersize = number_of_transfers,
|
||||
.sbsize = 2, /* Burst size: 8 transfers */
|
||||
.dbsize = 2, /* Burst size: 8 transfers */
|
||||
.swidth = 2, /* Source transfer width: word (32 bits) */
|
||||
.dwidth = 2, /* Destination transfer width: word (32 bits) */
|
||||
.s = gpdma_ahb_master_peripheral,
|
||||
.d = gpdma_ahb_master_memory,
|
||||
.si = 1,
|
||||
.di = 1,
|
||||
.prot1 = 0,
|
||||
.prot2 = 0,
|
||||
.prot3 = 0,
|
||||
.i = 0,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr gpdma::channel::Config config() {
|
||||
return {
|
||||
.e = 0,
|
||||
.srcperipheral = gpdma_src_peripheral,
|
||||
.destperipheral = gpdma_dest_peripheral,
|
||||
.flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl,
|
||||
.ie = 0,
|
||||
.itc = 0,
|
||||
.l = 0,
|
||||
.a = 0,
|
||||
.h = 0,
|
||||
};
|
||||
}
|
||||
|
||||
static gpdma::channel::LLI lli;
|
||||
|
||||
constexpr size_t channels_per_sample = 8;
|
||||
//constexpr size_t samples_per_frame = 40;
|
||||
//constexpr size_t channel_samples_per_frame = channels_per_sample * samples_per_frame;
|
||||
|
||||
void init() {
|
||||
}
|
||||
|
||||
void allocate() {
|
||||
//samples = new sample_t[channel_samples_per_frame];
|
||||
//lli = new gpdma::channel::LLI;
|
||||
lli.srcaddr = reinterpret_cast<uint32_t>(&LPC_ADC0->DR[0]);
|
||||
lli.destaddr = reinterpret_cast<uint32_t>(&shared_memory.touch_adc_frame.dr[0]);
|
||||
lli.lli = lli_pointer(&lli);
|
||||
lli.control = control(channels_per_sample);
|
||||
}
|
||||
|
||||
void free() {
|
||||
//delete samples;
|
||||
//delete lli;
|
||||
}
|
||||
|
||||
void enable() {
|
||||
const auto gpdma_config = config();
|
||||
gpdma_channel.configure(lli, gpdma_config);
|
||||
gpdma_channel.enable();
|
||||
}
|
||||
|
||||
bool is_enabled() {
|
||||
return gpdma_channel.is_enabled();
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel.disable_force();
|
||||
}
|
||||
|
||||
} /* namespace dma */
|
||||
} /* namespace touch */
|
52
firmware/baseband-tx/touch_dma.hpp
Normal file
52
firmware/baseband-tx/touch_dma.hpp
Normal file
@ -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 <cstddef>
|
||||
|
||||
namespace touch {
|
||||
namespace dma {
|
||||
|
||||
using sample_t = uint32_t;
|
||||
using buffer_t = buffer_t<sample_t>;
|
||||
|
||||
using Handler = void (*)();
|
||||
|
||||
void init();
|
||||
|
||||
void allocate();
|
||||
void free();
|
||||
|
||||
void enable();
|
||||
bool is_enabled();
|
||||
|
||||
void disable();
|
||||
|
||||
buffer_t wait_for_buffer();
|
||||
|
||||
} /* namespace dma */
|
||||
} /* namespace touch */
|
||||
|
||||
#endif/*__TOUCH_DMA_H__*/
|
BIN
firmware/baseband.bin
Normal file
BIN
firmware/baseband.bin
Normal file
Binary file not shown.
1
firmware/baseband/description
Normal file
1
firmware/baseband/description
Normal file
@ -0,0 +1 @@
|
||||
Basic RX/TX stuff for testing :)
|
@ -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<buffer.count; i++) {
|
||||
|
||||
//Sample generation 2.28M/10=228kHz
|
||||
if(s >= 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<FILTER_SIZE; j++) {
|
||||
val = (*src++);
|
||||
if (cur_output) val = -val;
|
||||
sample_buffer[idx++] += val;
|
||||
if (idx >= 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<buffer.count; i++) {
|
||||
|
||||
//Sample generation 2.28M/10 = 228kHz
|
||||
if (s >= 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<buffer.count; i++) {
|
||||
|
||||
//Sample generation 2.28M/10 = 228kHz
|
||||
if (s >= 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<buffer.count; i++) {
|
||||
|
||||
/*if (s > 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<baseband::sample_t, 8192>();
|
||||
|
||||
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<char*>(&ram_loop_fn), 32);
|
||||
loop_ptr = reinterpret_cast<fn_ptr>(&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;
|
||||
|
1
firmware/baseband/name
Normal file
1
firmware/baseband/name
Normal file
@ -0,0 +1 @@
|
||||
First module
|
@ -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(
|
||||
|
@ -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<float, sine_table_f32_period + 1> sine_table_f32 { {
|
||||
static constexpr std::array<float, sine_table_f32_period + 1> 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,
|
||||
|
@ -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 {
|
||||
|
Binary file not shown.
92
firmware/tools/make_baseband_file.py
Executable file
92
firmware/tools/make_baseband_file.py
Executable file
@ -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: <command> <module_name> <module_name>...
|
||||
"""
|
||||
|
||||
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')
|
Loading…
Reference in New Issue
Block a user