mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-01-26 14:36:17 -05:00
Merge fixing, commit to catch up on recent files
This commit is contained in:
parent
44638e504b
commit
6e496e2b26
@ -148,6 +148,8 @@ CPPSRC = main.cpp \
|
||||
encoder.cpp \
|
||||
lcd_ili9341.cpp \
|
||||
ui.cpp \
|
||||
ui_alphanum.cpp \
|
||||
ui_about.cpp \
|
||||
ui_text.cpp \
|
||||
ui_widget.cpp \
|
||||
ui_painter.cpp \
|
||||
@ -157,6 +159,9 @@ CPPSRC = main.cpp \
|
||||
ui_rssi.cpp \
|
||||
ui_channel.cpp \
|
||||
ui_audio.cpp \
|
||||
ui_lcr.cpp \
|
||||
ui_rds.cpp \
|
||||
ui_jammer.cpp \
|
||||
ui_font_fixed_8x16.cpp \
|
||||
ui_setup.cpp \
|
||||
ui_debug.cpp \
|
||||
|
@ -19,36 +19,34 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "irq_ipc.hpp"
|
||||
#ifndef __FILE_H__
|
||||
#define __FILE_H__
|
||||
|
||||
#include "event.hpp"
|
||||
#include "ff.h"
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
using namespace lpc43xx;
|
||||
class File {
|
||||
public:
|
||||
~File();
|
||||
|
||||
void m4txevent_interrupt_enable() {
|
||||
nvicEnableVector(M4CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M4TXEVENT_IRQ_PRIORITY));
|
||||
}
|
||||
bool open_for_append(const std::string file_path);
|
||||
bool close();
|
||||
|
||||
void m4txevent_interrupt_disable() {
|
||||
nvicDisableVector(M4CORE_IRQn);
|
||||
}
|
||||
bool is_ready();
|
||||
|
||||
extern "C" {
|
||||
bool read(void* const data, const size_t bytes_to_read);
|
||||
bool write(const void* const data, const size_t bytes_to_write);
|
||||
|
||||
CH_IRQ_HANDLER(M4Core_IRQHandler) {
|
||||
CH_IRQ_PROLOGUE();
|
||||
bool puts(const std::string string);
|
||||
|
||||
chSysLockFromIsr();
|
||||
events_flag_isr(EVT_MASK_APPLICATION);
|
||||
chSysUnlockFromIsr();
|
||||
bool sync();
|
||||
|
||||
creg::m4txevent::clear();
|
||||
private:
|
||||
const std::string file_path;
|
||||
|
||||
FIL f;
|
||||
};
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
}
|
||||
#endif/*__FILE_H__*/
|
||||
|
@ -106,7 +106,7 @@ void m4_switch(const char * hash) {
|
||||
|
||||
// Ask M4 to enter wait loop in RAM
|
||||
BasebandConfiguration baseband_switch {
|
||||
.mode = SWITCH,
|
||||
.mode = 255, // DEBUG
|
||||
.sampling_rate = 0,
|
||||
.decimation_factor = 1,
|
||||
};
|
||||
|
@ -62,7 +62,6 @@
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
|
||||
#include "irq_ipc.hpp"
|
||||
#include "irq_lcd_frame.hpp"
|
||||
#include "irq_controls.hpp"
|
||||
#include "irq_rtc.hpp"
|
||||
|
@ -75,31 +75,8 @@ uint32_t TransmitterModel::sampling_rate() const {
|
||||
return baseband_configuration.sampling_rate;
|
||||
}
|
||||
|
||||
void TransmitterModel::set_sampling_rate(uint32_t hz) {
|
||||
baseband_configuration.sampling_rate = hz;
|
||||
update_baseband_configuration();
|
||||
}
|
||||
|
||||
mode_type TransmitterModel::modulation() const {
|
||||
return baseband_configuration.mode;
|
||||
}
|
||||
|
||||
void TransmitterModel::set_modulation(mode_type v) {
|
||||
baseband_configuration.mode = v;
|
||||
update_modulation();
|
||||
}
|
||||
|
||||
uint32_t TransmitterModel::baseband_oversampling() const {
|
||||
// TODO: Rename decimation_factor.
|
||||
return baseband_configuration.decimation_factor;
|
||||
}
|
||||
|
||||
void TransmitterModel::set_baseband_oversampling(uint32_t v) {
|
||||
baseband_configuration.decimation_factor = v;
|
||||
update_baseband_configuration();
|
||||
}
|
||||
|
||||
void TransmitterModel::enable() {
|
||||
enabled_ = true;
|
||||
radio::set_direction(rf::Direction::Transmit);
|
||||
update_tuning_frequency();
|
||||
update_rf_amp();
|
||||
@ -107,23 +84,30 @@ void TransmitterModel::enable() {
|
||||
update_vga();
|
||||
update_baseband_bandwidth();
|
||||
update_baseband_configuration();
|
||||
radio::streaming_enable();
|
||||
}
|
||||
|
||||
void TransmitterModel::baseband_disable() {
|
||||
shared_memory.baseband_queue.push_and_wait(
|
||||
BasebandConfigurationMessage {
|
||||
.configuration = { },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void TransmitterModel::disable() {
|
||||
/* TODO: This is a dumb hack to stop baseband from working so hard. */
|
||||
BasebandConfigurationMessage message {
|
||||
.configuration = {
|
||||
.mode = NONE,
|
||||
.sampling_rate = 0,
|
||||
.decimation_factor = 1,
|
||||
}
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
enabled_ = false;
|
||||
baseband_disable();
|
||||
|
||||
// TODO: Responsibility for enabling/disabling the radio is muddy.
|
||||
// Some happens in ReceiverModel, some inside radio namespace.
|
||||
radio::disable();
|
||||
}
|
||||
|
||||
uint32_t TransmitterModel::baseband_oversampling() const {
|
||||
// TODO: Rename decimation_factor.
|
||||
return baseband_configuration.decimation_factor;
|
||||
}
|
||||
|
||||
int32_t TransmitterModel::tuning_offset() {
|
||||
return -(sampling_rate() / 4);
|
||||
}
|
||||
@ -148,8 +132,8 @@ void TransmitterModel::update_vga() {
|
||||
radio::set_vga_gain(vga_gain_db_);
|
||||
}
|
||||
|
||||
void TransmitterModel::update_modulation() {
|
||||
update_baseband_configuration();
|
||||
uint32_t TransmitterModel::modulation() const {
|
||||
return baseband_configuration.mode;
|
||||
}
|
||||
|
||||
void TransmitterModel::set_baseband_configuration(const BasebandConfiguration config) {
|
||||
@ -158,7 +142,12 @@ void TransmitterModel::set_baseband_configuration(const BasebandConfiguration co
|
||||
}
|
||||
|
||||
void TransmitterModel::update_baseband_configuration() {
|
||||
radio::streaming_disable();
|
||||
// TODO: Move more low-level radio control stuff to M4. It'll enable tighter
|
||||
// synchronization for things like wideband (sweeping) spectrum analysis, and
|
||||
// protocols that need quick RX/TX turn-around.
|
||||
|
||||
// Disabling baseband while changing sampling rates seems like a good idea...
|
||||
baseband_disable();
|
||||
|
||||
clock_manager.set_sampling_frequency(sampling_rate() * baseband_oversampling());
|
||||
update_tuning_frequency();
|
||||
@ -166,7 +155,5 @@ void TransmitterModel::update_baseband_configuration() {
|
||||
|
||||
BasebandConfigurationMessage message { baseband_configuration };
|
||||
shared_memory.baseband_queue.push(message);
|
||||
|
||||
radio::streaming_enable();
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,7 @@ public:
|
||||
) : clock_manager(clock_manager)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
rf::Frequency tuning_frequency() const;
|
||||
void set_tuning_frequency(rf::Frequency f);
|
||||
|
||||
@ -55,13 +55,10 @@ public:
|
||||
void set_vga(int32_t v_db);
|
||||
|
||||
uint32_t sampling_rate() const;
|
||||
void set_sampling_rate(uint32_t hz);
|
||||
|
||||
mode_type modulation() const;
|
||||
void set_modulation(mode_type v);
|
||||
|
||||
uint32_t modulation() const;
|
||||
|
||||
uint32_t baseband_oversampling() const;
|
||||
void set_baseband_oversampling(uint32_t v);
|
||||
|
||||
void enable();
|
||||
void disable();
|
||||
@ -69,12 +66,14 @@ public:
|
||||
void set_baseband_configuration(const BasebandConfiguration config);
|
||||
|
||||
private:
|
||||
rf::Frequency frequency_step_ { 25000 };
|
||||
bool enabled_ { false };
|
||||
bool rf_amp_ { true };
|
||||
int32_t lna_gain_db_ { 0 };
|
||||
uint32_t baseband_bandwidth_ { max2837::filter::bandwidth_minimum };
|
||||
int32_t vga_gain_db_ { 8 };
|
||||
BasebandConfiguration baseband_configuration {
|
||||
.mode = NONE,
|
||||
.mode = 1,
|
||||
.sampling_rate = 2280000,
|
||||
.decimation_factor = 1,
|
||||
};
|
||||
@ -89,6 +88,8 @@ private:
|
||||
void update_vga();
|
||||
void update_modulation();
|
||||
void update_baseband_configuration();
|
||||
|
||||
void baseband_disable();
|
||||
};
|
||||
|
||||
#endif/*__TRANSMITTER_MODEL_H__*/
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "ymdata.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "ui_about.hpp"
|
||||
#include "touch.hpp"
|
||||
@ -44,23 +45,21 @@ using namespace portapack;
|
||||
namespace ui {
|
||||
|
||||
void AboutView::on_show() {
|
||||
auto& message_map = context().message_map();
|
||||
|
||||
// Just in case
|
||||
message_map.unregister_handler(Message::ID::DisplayFrameSync);
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync);
|
||||
|
||||
// "Vertical blank interrupt"
|
||||
message_map.register_handler(Message::ID::DisplayFrameSync,
|
||||
EventDispatcher::message_map().register_handler(Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
update();
|
||||
}
|
||||
);
|
||||
|
||||
// Just in case
|
||||
message_map.unregister_handler(Message::ID::FIFOSignal);
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::FIFOSignal);
|
||||
|
||||
// Handle baseband asking to fill up FIFO
|
||||
message_map.register_handler(Message::ID::FIFOSignal,
|
||||
EventDispatcher::message_map().register_handler(Message::ID::FIFOSignal,
|
||||
[this](Message* const p) {
|
||||
FIFODataMessage datamessage;
|
||||
const auto message = static_cast<const FIFOSignalMessage*>(p);
|
||||
@ -349,6 +348,7 @@ void AboutView::update() {
|
||||
refresh_cnt++;
|
||||
}
|
||||
|
||||
// Slowly increase volume to avoid jumpscare
|
||||
if (headphone_vol < (70<<2)) {
|
||||
portapack::audio_codec.set_headphone_volume(volume_t::decibel((headphone_vol/4) - 99) + wolfson::wm8731::headphone_gain_range.max);
|
||||
headphone_vol++;
|
||||
@ -370,14 +370,13 @@ void AboutView::ym_init() {
|
||||
}
|
||||
|
||||
AboutView::AboutView(
|
||||
NavigationView& nav,
|
||||
TransmitterModel& transmitter_model
|
||||
) : transmitter_model(transmitter_model)
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
uint8_t p, c;
|
||||
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = PLAY_AUDIO,
|
||||
.mode = 5,
|
||||
.sampling_rate = 1536000,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
@ -412,8 +411,7 @@ AboutView::AboutView(
|
||||
ym_init();
|
||||
|
||||
button_ok.on_select = [this,&nav](Button&){
|
||||
auto& message_map = context().message_map();
|
||||
message_map.unregister_handler(Message::ID::DisplayFrameSync);
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync);
|
||||
if (framebuffer) chHeapFree(framebuffer); // Do NOT forget this
|
||||
nav.pop();
|
||||
};
|
||||
|
@ -34,7 +34,7 @@ namespace ui {
|
||||
|
||||
class AboutView : public View {
|
||||
public:
|
||||
AboutView(NavigationView& nav, TransmitterModel& transmitter_model);
|
||||
AboutView(NavigationView& nav);
|
||||
~AboutView();
|
||||
|
||||
void on_show() override;
|
||||
@ -54,8 +54,6 @@ private:
|
||||
bool same;
|
||||
} ymreg_t;
|
||||
|
||||
TransmitterModel& transmitter_model;
|
||||
|
||||
uint16_t headphone_vol = 5<<2;
|
||||
|
||||
ymreg_t ym_regs[14];
|
||||
|
@ -24,19 +24,17 @@
|
||||
#include "ui_spectrum.hpp"
|
||||
#include "ui_console.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
#include "ff.h"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "ui_receiver.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
AFSKRXView::AFSKRXView(
|
||||
NavigationView& nav,
|
||||
ReceiverModel& receiver_model
|
||||
) : receiver_model(receiver_model)
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
add_children({ {
|
||||
&button_done,
|
||||
@ -48,7 +46,7 @@ AFSKRXView::AFSKRXView(
|
||||
};
|
||||
|
||||
receiver_model.set_baseband_configuration({
|
||||
.mode = RX_AFSK,
|
||||
.mode = 6,
|
||||
.sampling_rate = 3072000,
|
||||
.decimation_factor = 4,
|
||||
});
|
||||
@ -60,8 +58,7 @@ AFSKRXView::~AFSKRXView() {
|
||||
}
|
||||
|
||||
void AFSKRXView::on_show() {
|
||||
auto& message_map = context().message_map();
|
||||
message_map.register_handler(Message::ID::AFSKData,
|
||||
EventDispatcher::message_map().register_handler(Message::ID::AFSKData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const AFSKDataMessage*>(p);
|
||||
this->on_data_afsk(*message);
|
||||
@ -72,8 +69,7 @@ void AFSKRXView::on_show() {
|
||||
}
|
||||
|
||||
void AFSKRXView::on_hide() {
|
||||
auto& message_map = context().message_map();
|
||||
message_map.unregister_handler(Message::ID::AFSKData);
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::AFSKData);
|
||||
}
|
||||
|
||||
void AFSKRXView::on_data_afsk(const AFSKDataMessage& message) {
|
||||
|
@ -42,7 +42,7 @@ namespace ui {
|
||||
|
||||
class AFSKRXView : public View {
|
||||
public:
|
||||
AFSKRXView(NavigationView& nav, ReceiverModel& receiver_model);
|
||||
AFSKRXView(NavigationView& nav);
|
||||
~AFSKRXView();
|
||||
|
||||
void focus() override;
|
||||
@ -51,8 +51,6 @@ public:
|
||||
void on_hide() override;
|
||||
|
||||
private:
|
||||
ReceiverModel& receiver_model;
|
||||
|
||||
std::unique_ptr<Widget> widget_content;
|
||||
|
||||
Button button_done {
|
||||
|
@ -66,9 +66,8 @@ void AFSKSetupView::updfreq(rf::Frequency f) {
|
||||
}
|
||||
|
||||
AFSKSetupView::AFSKSetupView(
|
||||
NavigationView& nav,
|
||||
TransmitterModel& transmitter_model
|
||||
) : transmitter_model(transmitter_model)
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
uint8_t rpt;
|
||||
|
||||
|
@ -36,15 +36,13 @@ namespace ui {
|
||||
|
||||
class AFSKSetupView : public View {
|
||||
public:
|
||||
AFSKSetupView(NavigationView& nav, TransmitterModel& transmitter_model);
|
||||
AFSKSetupView(NavigationView& nav);
|
||||
|
||||
void updfreq(rf::Frequency f);
|
||||
void focus() override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
TransmitterModel& transmitter_model;
|
||||
|
||||
Text text_title {
|
||||
{ 40, 32, 160, 16 },
|
||||
"AFSK modulator setup"
|
||||
|
@ -110,7 +110,7 @@ void AlphanumView::set_uppercase() {
|
||||
|
||||
size_t n = 0;
|
||||
for(auto& button : buttons) {
|
||||
add_child(&button);
|
||||
//add_child(&button);
|
||||
const std::string label {
|
||||
key_caps[n]
|
||||
};
|
||||
@ -125,7 +125,7 @@ void AlphanumView::set_lowercase() {
|
||||
|
||||
size_t n = 0;
|
||||
for(auto& button : buttons) {
|
||||
add_child(&button);
|
||||
//add_child(&button);
|
||||
const std::string label {
|
||||
key_caps[n]
|
||||
};
|
||||
|
@ -245,20 +245,6 @@ void RegistersView::focus() {
|
||||
button_done.focus();
|
||||
}
|
||||
|
||||
void DebugLCRView::paint(Painter& painter) {
|
||||
const Point offset = {
|
||||
static_cast<Coord>(32),
|
||||
static_cast<Coord>(32)
|
||||
};
|
||||
|
||||
const auto text = to_string_hex(fr, 2);
|
||||
painter.draw_string(
|
||||
screen_pos() + offset,
|
||||
style(),
|
||||
text
|
||||
);
|
||||
}
|
||||
|
||||
char hexify(char in) {
|
||||
if (in > 9) in += 7;
|
||||
return in + 0x30;
|
||||
@ -301,26 +287,26 @@ void DebugLCRView::focus() {
|
||||
|
||||
DebugMenuView::DebugMenuView(NavigationView& nav) {
|
||||
add_items<8>({ {
|
||||
{ "Memory", [&nav](){ nav.push<DebugMemoryView>(); } },
|
||||
{ "Radio State", [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "SD Card", [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "RFFC5072", [&nav](){ nav.push<RegistersView>(
|
||||
{ "Memory", ui::Color::white(), [&nav](){ nav.push<DebugMemoryView>(); } },
|
||||
{ "Radio State", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "SD Card", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "RFFC5072", ui::Color::white(), [&nav](){ nav.push<RegistersView>(
|
||||
"RFFC5072", RegistersWidgetConfig { 31, 2, 4, 4 },
|
||||
[](const size_t register_number) { return radio::first_if.read(register_number); }
|
||||
); } },
|
||||
{ "MAX2837", [&nav](){ nav.push<RegistersView>(
|
||||
{ "MAX2837", ui::Color::white(), [&nav](){ nav.push<RegistersView>(
|
||||
"MAX2837", RegistersWidgetConfig { 32, 2, 3, 4 },
|
||||
[](const size_t register_number) { return radio::second_if.read(register_number); }
|
||||
); } },
|
||||
{ "Si5351C", [&nav](){ nav.push<RegistersView>(
|
||||
{ "Si5351C", ui::Color::white(), [&nav](){ nav.push<RegistersView>(
|
||||
"Si5351C", RegistersWidgetConfig { 96, 2, 2, 8 },
|
||||
[](const size_t register_number) { return portapack::clock_generator.read_register(register_number); }
|
||||
); } },
|
||||
{ "WM8731", [&nav](){ nav.push<RegistersView>(
|
||||
{ "WM8731", ui::Color::white(), [&nav](){ nav.push<RegistersView>(
|
||||
"WM8731", RegistersWidgetConfig { wolfson::wm8731::reg_count, 1, 3, 4 },
|
||||
[](const size_t register_number) { return portapack::audio_codec.read(register_number); }
|
||||
); } },
|
||||
{ "Temperature", [&nav](){ nav.push<TemperatureView>(); } },
|
||||
{ "Temperature", ui::Color::white(), [&nav](){ nav.push<TemperatureView>(); } },
|
||||
} });
|
||||
on_left = [&nav](){ nav.pop(); };
|
||||
}
|
||||
|
@ -246,8 +246,6 @@ public:
|
||||
|
||||
void focus() override;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
Text text_lcr1 {
|
||||
{ 16, 32, 208, 8 },
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "radio.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
@ -37,7 +39,7 @@
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace hackrf::one;
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -146,9 +148,8 @@ void JammerView::updfreq(uint8_t id, rf::Frequency f) {
|
||||
}
|
||||
|
||||
JammerView::JammerView(
|
||||
NavigationView& nav,
|
||||
TransmitterModel& transmitter_model
|
||||
) : transmitter_model(transmitter_model)
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
|
||||
static constexpr Style style_val {
|
||||
@ -169,7 +170,11 @@ JammerView::JammerView(
|
||||
.foreground = Color::grey(),
|
||||
};
|
||||
|
||||
transmitter_model.set_modulation(TX_JAMMER);
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = 3,
|
||||
.sampling_rate = 1536000, // ?
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
|
||||
add_children({ {
|
||||
&text_type,
|
||||
@ -232,11 +237,9 @@ JammerView::JammerView(
|
||||
button_transmit.on_select = [this,&transmitter_model](Button&) {
|
||||
uint8_t i = 0;
|
||||
rf::Frequency t, range_lower;
|
||||
auto& message_map = context().message_map();
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::Retune);
|
||||
|
||||
message_map.unregister_handler(Message::ID::Retune);
|
||||
|
||||
message_map.register_handler(Message::ID::Retune,
|
||||
EventDispatcher::message_map().register_handler(Message::ID::Retune,
|
||||
[this,&transmitter_model](Message* const p) {
|
||||
const auto message = static_cast<const RetuneMessage*>(p);
|
||||
if (message->freq > 0) {
|
||||
|
@ -36,7 +36,7 @@ namespace ui {
|
||||
|
||||
class JammerView : public View {
|
||||
public:
|
||||
JammerView(NavigationView& nav, TransmitterModel& transmitter_model);
|
||||
JammerView(NavigationView& nav);
|
||||
~JammerView();
|
||||
|
||||
void updfreq(uint8_t id, rf::Frequency f);
|
||||
@ -117,7 +117,6 @@ private:
|
||||
|
||||
bool jamming = false;
|
||||
rf::Frequency f;
|
||||
TransmitterModel& transmitter_model;
|
||||
|
||||
Text text_type {
|
||||
{ 1 * 8, 1 * 16, 40, 16 },
|
||||
|
@ -31,8 +31,11 @@
|
||||
#include "ff.h"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "event_m0.hpp"
|
||||
#include "radio.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
@ -40,7 +43,7 @@
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace hackrf::one;
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -176,9 +179,8 @@ void LCRView::paint(Painter& painter) {
|
||||
}
|
||||
|
||||
LCRView::LCRView(
|
||||
NavigationView& nav,
|
||||
TransmitterModel& transmitter_model
|
||||
) : transmitter_model(transmitter_model)
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
char finalstr[24] = {0};
|
||||
|
||||
@ -188,7 +190,12 @@ LCRView::LCRView(
|
||||
.foreground = Color::black(),
|
||||
};
|
||||
|
||||
transmitter_model.set_modulation(TX_LCR);
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = 1,
|
||||
.sampling_rate = 1536000, // Is this right ?
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
|
||||
transmitter_model.set_tuning_frequency(portapack::persistent_memory::tuned_frequency());
|
||||
memset(litteral, 0, 5*8);
|
||||
memset(rgsb, 0, 5);
|
||||
@ -277,8 +284,6 @@ LCRView::LCRView(
|
||||
};
|
||||
|
||||
button_transmit.on_select = [this,&transmitter_model](Button&){
|
||||
auto& message_map = context().message_map();
|
||||
|
||||
make_frame();
|
||||
|
||||
shared_memory.afsk_samples_per_bit = 228000/portapack::persistent_memory::afsk_bitrate();
|
||||
@ -293,7 +298,7 @@ LCRView::LCRView(
|
||||
shared_memory.afsk_transmit_done = false;
|
||||
shared_memory.afsk_repeat = (portapack::persistent_memory::afsk_config() >> 8) & 0xFF;
|
||||
|
||||
message_map.register_handler(Message::ID::TXDone,
|
||||
EventDispatcher::message_map().register_handler(Message::ID::TXDone,
|
||||
[this,&transmitter_model](Message* const p) {
|
||||
char str[8];
|
||||
const auto message = static_cast<const TXDoneMessage*>(p);
|
||||
@ -319,7 +324,7 @@ LCRView::LCRView(
|
||||
};
|
||||
|
||||
button_txsetup.on_select = [&nav, &transmitter_model](Button&){
|
||||
nav.push(new AFSKSetupView { nav, transmitter_model });
|
||||
nav.push(new AFSKSetupView { nav });
|
||||
};
|
||||
|
||||
button_exit.on_select = [&nav](Button&){
|
||||
|
@ -36,7 +36,7 @@ namespace ui {
|
||||
|
||||
class LCRView : public View {
|
||||
public:
|
||||
LCRView(NavigationView& nav, TransmitterModel& transmitter_model);
|
||||
LCRView(NavigationView& nav);
|
||||
~LCRView();
|
||||
|
||||
void make_frame();
|
||||
@ -63,7 +63,6 @@ private:
|
||||
char lcrframe[256];
|
||||
char lcrframe_f[256];
|
||||
rf::Frequency f;
|
||||
TransmitterModel& transmitter_model;
|
||||
|
||||
Text text_status {
|
||||
{ 168, 196, 64, 16 },
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -24,10 +25,12 @@
|
||||
#include "ch.h"
|
||||
|
||||
#include "ff.h"
|
||||
#include "event_m0.hpp"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
@ -45,18 +48,17 @@ void LoadModuleView::paint(Painter& painter) {
|
||||
}
|
||||
|
||||
void LoadModuleView::on_hide() {
|
||||
auto& message_map = context().message_map();
|
||||
message_map.unregister_handler(Message::ID::ReadyForSwitch);
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::ReadyForSwitch);
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::ModuleID);
|
||||
}
|
||||
|
||||
void LoadModuleView::on_show() {
|
||||
// Ask for MD5 signature and compare
|
||||
ModuleIDMessage message;
|
||||
auto& message_map = context().message_map();
|
||||
|
||||
//message_map.unregister_handler(Message::ID::ModuleID);
|
||||
|
||||
message_map.unregister_handler(Message::ID::ModuleID);
|
||||
|
||||
message_map.register_handler(Message::ID::ModuleID,
|
||||
EventDispatcher::message_map().register_handler(Message::ID::ModuleID,
|
||||
[this](Message* const p) {
|
||||
uint8_t c;
|
||||
const auto message = static_cast<const ModuleIDMessage*>(p);
|
||||
@ -78,13 +80,66 @@ void LoadModuleView::on_show() {
|
||||
shared_memory.baseband_queue.push(message);
|
||||
}
|
||||
|
||||
int LoadModuleView::load_image() {
|
||||
const char magic[6] = {'P', 'P', 'M', ' ', 0x02, 0x00};
|
||||
UINT bw;
|
||||
uint8_t i;
|
||||
uint32_t cnt;
|
||||
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
|
||||
if (f_opendir(&rootdir, "/") == FR_OK) {
|
||||
for (;;) {
|
||||
res = f_readdir(&rootdir, &modinfo);
|
||||
if (res != FR_OK || modinfo.fname[0] == 0) break; // Reached last file, abort
|
||||
// Only care about files with .bin extension
|
||||
if ((!(modinfo.fattrib & AM_DIR)) && (modinfo.fname[9] == 'B') && (modinfo.fname[10] == 'I') && (modinfo.fname[11] == 'N')) {
|
||||
res = f_open(&modfile, modinfo.fname, FA_OPEN_EXISTING | FA_READ);
|
||||
if (res != FR_OK) return 0;
|
||||
// Magic bytes and version check
|
||||
f_read(&modfile, &md5sum, 6, &bw);
|
||||
for (i = 0; i < 6; i++)
|
||||
if (md5sum[i] != magic[i]) break;
|
||||
if (i == 6) {
|
||||
f_lseek(&modfile, 26);
|
||||
f_read(&modfile, &md5sum, 16, &bw);
|
||||
for (i = 0; i < 16; i++)
|
||||
if (md5sum[i] != _hash[i]) break;
|
||||
// f_read can't read more than 512 bytes at a time ?
|
||||
if (i == 16) {
|
||||
f_lseek(&modfile, 512);
|
||||
for (cnt = 0; cnt < 64; cnt++) {
|
||||
if (f_read(&modfile, reinterpret_cast<void*>(portapack::memory::map::m4_code.base() + (cnt * 512)), 512, &bw)) return 0;
|
||||
}
|
||||
f_close(&modfile);
|
||||
f_closedir(&rootdir);
|
||||
LPC_RGU->RESET_CTRL[0] = (1 << 13);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
f_close(&modfile);
|
||||
}
|
||||
}
|
||||
f_closedir(&rootdir);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LoadModuleView::loadmodule() {
|
||||
auto& message_map = context().message_map();
|
||||
message_map.register_handler(Message::ID::ReadyForSwitch,
|
||||
//message_map.unregister_handler(Message::ID::ReadyForSwitch);
|
||||
|
||||
EventDispatcher::message_map().register_handler(Message::ID::ReadyForSwitch,
|
||||
[this](Message* const p) {
|
||||
(void)p;
|
||||
if (m4_load_image()) {
|
||||
text_info.set("Module loaded :)");
|
||||
if (load_image()) {
|
||||
text_info.set(to_string_hex(*((unsigned int*)0x10080000),8));
|
||||
//text_infob.set(to_string_hex(*((unsigned int*)0x10080004),8));
|
||||
text_infob.set("Module loaded :)");
|
||||
_mod_loaded = true;
|
||||
} else {
|
||||
text_info.set("Module not found :(");
|
||||
@ -104,6 +159,7 @@ LoadModuleView::LoadModuleView(
|
||||
{
|
||||
add_children({ {
|
||||
&text_info,
|
||||
&text_infob,
|
||||
&button_ok
|
||||
} });
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@ -31,7 +32,7 @@ namespace ui {
|
||||
|
||||
class LoadModuleView : public View {
|
||||
public:
|
||||
LoadModuleView(NavigationView& nav, const char * hash, View* new_view);
|
||||
LoadModuleView(NavigationView& nav, const char * hash, View * new_view);
|
||||
void loadmodule();
|
||||
|
||||
void on_show() override;
|
||||
@ -40,12 +41,17 @@ public:
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
int load_image(void);
|
||||
const char * _hash;
|
||||
bool _mod_loaded = false;
|
||||
|
||||
Text text_info {
|
||||
{ 8, 64, 224, 16 },
|
||||
"Searching module..."
|
||||
"-"
|
||||
};
|
||||
Text text_infob {
|
||||
{ 8, 64+16, 224, 16 },
|
||||
"-"
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
|
@ -108,6 +108,10 @@ View* NavigationView::push_view(std::unique_ptr<View> new_view) {
|
||||
return p;
|
||||
}
|
||||
|
||||
void NavigationView::push(View* v) {
|
||||
push_view(std::unique_ptr<View>(v));
|
||||
}
|
||||
|
||||
void NavigationView::pop() {
|
||||
// Can't pop last item from stack.
|
||||
if( view_stack.size() > 1 ) {
|
||||
@ -149,9 +153,9 @@ void NavigationView::focus() {
|
||||
|
||||
TranspondersMenuView::TranspondersMenuView(NavigationView& nav) {
|
||||
add_items<3>({ {
|
||||
{ "AIS: Boats", [&nav](){ nav.push<AISAppView>(); } },
|
||||
{ "ERT: Utility Meters", [&nav](){ nav.push<ERTAppView>(); } },
|
||||
{ "TPMS: Cars", [&nav](){ nav.push<TPMSAppView>(); } },
|
||||
{ "AIS: Boats", ui::Color::white(), [&nav](){ nav.push<AISAppView>(); } },
|
||||
{ "ERT: Utility Meters", ui::Color::white(), [&nav](){ nav.push<ERTAppView>(); } },
|
||||
{ "TPMS: Cars", ui::Color::white(), [&nav](){ nav.push<TPMSAppView>(); } },
|
||||
} });
|
||||
}
|
||||
|
||||
@ -159,8 +163,8 @@ TranspondersMenuView::TranspondersMenuView(NavigationView& nav) {
|
||||
|
||||
ReceiverMenuView::ReceiverMenuView(NavigationView& nav) {
|
||||
add_items<2>({ {
|
||||
{ "Audio", [&nav](){ nav.push<ReceiverView>(); } },
|
||||
{ "Transponders", [&nav](){ nav.push<TranspondersMenuView>(); } },
|
||||
{ "Audio", ui::Color::white(), [&nav](){ nav.push<ReceiverView>(); } },
|
||||
{ "Transponders", ui::Color::white(), [&nav](){ nav.push<TranspondersMenuView>(); } },
|
||||
} });
|
||||
}
|
||||
|
||||
@ -168,24 +172,24 @@ ReceiverMenuView::ReceiverMenuView(NavigationView& nav) {
|
||||
|
||||
SystemMenuView::SystemMenuView(NavigationView& nav) {
|
||||
add_items<10>({ {
|
||||
{ "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 ReceiverMenuView { nav, receiver_model }}); } },
|
||||
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(false); } },
|
||||
{ "Receiver", ui::Color::cyan(), [&nav](){ nav.push<LoadModuleView>(md5_baseband, new ReceiverMenuView(nav)); } },
|
||||
//{ "Nordic/BTLE RX", ui::Color::cyan(), [&nav](){ nav.push(new NotImplementedView { nav }); } },
|
||||
{ "Jammer", ui::Color::white(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband, new JammerView { nav, transmitter_model }}); } },
|
||||
{ "Jammer", ui::Color::white(), [&nav](){ nav.push<LoadModuleView>(md5_baseband, new JammerView(nav)); } },
|
||||
//{ "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 LoadModuleView { nav, md5_baseband, new WhistleView { nav, transmitter_model }}); } },
|
||||
//{ "SIGFOX RX", ui::Color::orange(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband, new SIGFRXView { nav, receiver_model }}); } },
|
||||
{ "RDS TX", ui::Color::yellow(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband_tx, new RDSView { nav, transmitter_model }}); } },
|
||||
{ "Xylos TX", ui::Color::orange(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband_tx, new XylosView { nav, transmitter_model }}); } },
|
||||
{ "RDS TX", ui::Color::yellow(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, new RDSView(nav)); } },
|
||||
{ "Xylos TX", ui::Color::orange(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, new XylosView(nav)); } },
|
||||
//{ "Xylos RX", ui::Color::green(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband_tx, new XylosRXView { nav, receiver_model }}); } },
|
||||
//{ "AFSK RX", ui::Color::cyan(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband, new AFSKRXView { nav, receiver_model }}); } },
|
||||
{ "TEDI/LCR TX", ui::Color::yellow(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband_tx, new LCRView { nav, transmitter_model }}); } },
|
||||
{ "TEDI/LCR TX", ui::Color::yellow(), [&nav](){ nav.push<LoadModuleView>(md5_baseband_tx, new LCRView(nav)); } },
|
||||
//{ "Numbers station", ui::Color::purple(), [&nav](){ nav.push(new LoadModuleView { nav, md5_baseband_tx, new NumbersStationView { nav, transmitter_model }}); } },
|
||||
{ "Setup", ui::Color::white(), [&nav](){ nav.push(new SetupMenuView { nav }); } },
|
||||
{ "About", ui::Color::white(), [&nav](){ nav.push(new AboutView { nav, transmitter_model }); } },
|
||||
{ "Debug", ui::Color::white(), [&nav](){ nav.push(new DebugMenuView { nav }); } },
|
||||
{ "HackRF", ui::Color::white(), [&nav](){ nav.push(new HackRFFirmwareView { nav }); } },
|
||||
{ "Setup", ui::Color::white(), [&nav](){ nav.push<SetupMenuView>(); } },
|
||||
{ "About", ui::Color::white(), [&nav](){ nav.push<AboutView>(); } },
|
||||
{ "Debug", ui::Color::white(), [&nav](){ nav.push<DebugMenuView>(); } },
|
||||
{ "HackRF", ui::Color::white(), [&nav](){ nav.push<HackRFFirmwareView>(); } },
|
||||
} });
|
||||
}
|
||||
|
||||
@ -266,6 +270,41 @@ HackRFFirmwareView::HackRFFirmwareView(NavigationView& nav) {
|
||||
} });
|
||||
}
|
||||
|
||||
/* PlayDeadView **********************************************************/
|
||||
|
||||
void PlayDeadView::focus() {
|
||||
button_done.focus();
|
||||
}
|
||||
|
||||
PlayDeadView::PlayDeadView(NavigationView& nav, bool booting) {
|
||||
_booting = booting;
|
||||
persistent_memory::set_playing_dead(0x59);
|
||||
|
||||
add_children({ {
|
||||
&text_playdead1,
|
||||
&text_playdead2,
|
||||
&button_done,
|
||||
} });
|
||||
|
||||
button_done.on_dir = [this,&nav](Button&, KeyEvent key){
|
||||
sequence = (sequence<<3) | static_cast<std::underlying_type<KeyEvent>::type>(key);
|
||||
};
|
||||
|
||||
button_done.on_select = [this,&nav](Button&){
|
||||
if (sequence == persistent_memory::playdead_sequence()) {
|
||||
persistent_memory::set_playing_dead(0);
|
||||
if (_booting) {
|
||||
nav.pop();
|
||||
nav.push<SystemMenuView>();
|
||||
} else {
|
||||
nav.pop();
|
||||
}
|
||||
} else {
|
||||
sequence = 0;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void HackRFFirmwareView::focus() {
|
||||
button_no.focus();
|
||||
}
|
||||
|
@ -82,6 +82,8 @@ public:
|
||||
T* push(Args&&... args) {
|
||||
return reinterpret_cast<T*>(push_view(std::unique_ptr<View>(new T(*this, std::forward<Args>(args)...))));
|
||||
}
|
||||
|
||||
void push(View* v);
|
||||
|
||||
void pop();
|
||||
|
||||
@ -120,6 +122,29 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
class PlayDeadView : public View {
|
||||
public:
|
||||
PlayDeadView(NavigationView& nav, bool booting);
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
bool _booting;
|
||||
uint32_t sequence = 0;
|
||||
Text text_playdead1 {
|
||||
{ 6 * 8, 7 * 16, 14 * 8, 16 },
|
||||
"Firmware error"
|
||||
};
|
||||
Text text_playdead2 {
|
||||
{ 6 * 8, 9 * 16, 16 * 8, 16 },
|
||||
"0x1400_0000 : 2C"
|
||||
};
|
||||
|
||||
Button button_done {
|
||||
{ 240, 0, 1, 1 },
|
||||
""
|
||||
};
|
||||
};
|
||||
|
||||
class ReceiverMenuView : public MenuView {
|
||||
public:
|
||||
ReceiverMenuView(NavigationView& nav);
|
||||
|
@ -34,7 +34,7 @@
|
||||
|
||||
#include <cstring>
|
||||
|
||||
using namespace hackrf::one;
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -134,9 +134,8 @@ void RDSView::paint(Painter& painter) {
|
||||
}
|
||||
|
||||
RDSView::RDSView(
|
||||
NavigationView& nav,
|
||||
TransmitterModel& transmitter_model
|
||||
) : transmitter_model(transmitter_model)
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
transmitter_model.set_tuning_frequency(93000000);
|
||||
strcpy(psname, "TEST1234");
|
||||
|
@ -86,7 +86,7 @@ private:
|
||||
*/
|
||||
class RDSView : public View {
|
||||
public:
|
||||
RDSView(NavigationView& nav, TransmitterModel& transmitter_model);
|
||||
RDSView(NavigationView& nav);
|
||||
~RDSView();
|
||||
|
||||
void focus() override;
|
||||
@ -94,7 +94,6 @@ public:
|
||||
|
||||
private:
|
||||
char psname[9];
|
||||
TransmitterModel& transmitter_model;
|
||||
|
||||
Text text_title {
|
||||
{ 76, 16, 88, 16 },
|
||||
|
@ -20,13 +20,16 @@
|
||||
*/
|
||||
|
||||
#include "ui_setup.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
#include "portapack.hpp"
|
||||
using portapack::receiver_model;
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -167,12 +170,6 @@ void AntennaBiasSetupView::focus() {
|
||||
button_done.focus();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void AboutView::focus() {
|
||||
button_ok.focus();
|
||||
}
|
||||
|
||||
void SetTouchCalibView::focus() {
|
||||
button_ok.focus();
|
||||
}
|
||||
@ -433,13 +430,13 @@ void ModInfoView::focus() {
|
||||
|
||||
SetupMenuView::SetupMenuView(NavigationView& nav) {
|
||||
add_items<7>({ {
|
||||
{ "SD card modules", ui::Color::white(), [&nav](){ nav.push(new ModInfoView { nav }); } },
|
||||
{ "Date/Time", ui::Color::white(), [&nav](){ nav.push(new SetDateTimeView { nav }); } },
|
||||
{ "SD card modules", ui::Color::white(), [&nav](){ nav.push<ModInfoView>(); } },
|
||||
{ "Date/Time", ui::Color::white(), [&nav](){ nav.push<SetDateTimeView>(); } },
|
||||
{ "Frequency correction", ui::Color::white(), [&nav](){ nav.push<SetFrequencyCorrectionView>(); } },
|
||||
{ "Antenna Bias Voltage", [&nav](){ nav.push<AntennaBiasSetupView>(); } },
|
||||
{ "Touch screen", ui::Color::white(), [&nav](){ nav.push(new SetTouchCalibView { nav }); } },
|
||||
{ "Play dead", ui::Color::red(), [&nav](){ nav.push(new SetPlayDeadView { nav }); } },
|
||||
{ "UI", ui::Color::white(), [&nav](){ nav.push(new SetUIView { nav }); } },
|
||||
{ "Antenna Bias Voltage", ui::Color::white(), [&nav](){ nav.push<AntennaBiasSetupView>(); } },
|
||||
{ "Touch screen", ui::Color::white(), [&nav](){ nav.push<SetTouchCalibView>(); } },
|
||||
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<SetPlayDeadView>(); } },
|
||||
{ "UI", ui::Color::white(), [&nav](){ nav.push<SetUIView>(); } },
|
||||
} });
|
||||
on_left = [&nav](){ nav.pop(); };
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_menu.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ff.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@ -252,6 +253,132 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
class SetUIView : public View {
|
||||
public:
|
||||
SetUIView(NavigationView& nav);
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
Checkbox checkbox_showsplash {
|
||||
{ 3 * 8, 2 * 16},
|
||||
"Show splash"
|
||||
};
|
||||
|
||||
Checkbox checkbox_bloff {
|
||||
{ 3 * 8, 4 * 16},
|
||||
"Backlight off after:"
|
||||
};
|
||||
|
||||
OptionsField options_bloff {
|
||||
{ 10 * 8, 5 * 16 + 4 },
|
||||
10,
|
||||
{
|
||||
{ "5 seconds ", 0 },
|
||||
{ "15 seconds", 1 },
|
||||
{ "1 minute ", 2 },
|
||||
{ "5 minutes ", 3 },
|
||||
{ "10 minutes", 4 }
|
||||
}
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 4 * 8, 272, 64, 24 },
|
||||
"Ok"
|
||||
};
|
||||
};
|
||||
|
||||
class SetPlayDeadView : public View {
|
||||
public:
|
||||
SetPlayDeadView(NavigationView& nav);
|
||||
void focus() override;
|
||||
private:
|
||||
bool entermode = false;
|
||||
uint32_t sequence = 0;
|
||||
uint8_t keycount, key_code;
|
||||
char sequence_txt[11];
|
||||
|
||||
Text text_sequence {
|
||||
{ 64, 32, 14 * 8, 16 },
|
||||
"Enter sequence",
|
||||
};
|
||||
|
||||
Button button_enter {
|
||||
{ 16, 192, 96, 24 },
|
||||
"Enter"
|
||||
};
|
||||
Button button_cancel {
|
||||
{ 128, 192, 96, 24 },
|
||||
"Cancel"
|
||||
};
|
||||
};
|
||||
|
||||
class ModInfoView : public View {
|
||||
public:
|
||||
ModInfoView(NavigationView& nav);
|
||||
void focus() override;
|
||||
void on_show() override;
|
||||
|
||||
private:
|
||||
void update_infos(uint8_t modn);
|
||||
|
||||
typedef struct moduleinfo_t{
|
||||
char filename[9];
|
||||
uint16_t version;
|
||||
uint32_t size;
|
||||
char md5[16];
|
||||
char name[16];
|
||||
char description[214];
|
||||
} moduleinfo_t;
|
||||
|
||||
moduleinfo_t module_list[8]; // 8 max for now
|
||||
|
||||
Text text_modcount {
|
||||
{ 2 * 8, 1 * 16, 18 * 8, 16 },
|
||||
"Searching..."
|
||||
};
|
||||
|
||||
OptionsField option_modules {
|
||||
{ 2 * 8, 2 * 16 },
|
||||
24,
|
||||
{ { "-", 0 }
|
||||
}
|
||||
};
|
||||
|
||||
Text text_name {
|
||||
{ 2 * 8, 4 * 16, 5 * 8, 16 },
|
||||
"Name:"
|
||||
};
|
||||
Text text_namestr {
|
||||
{ 8 * 8, 4 * 16, 16 * 8, 16 },
|
||||
"..."
|
||||
};
|
||||
Text text_size {
|
||||
{ 2 * 8, 5 * 16, 5 * 8, 16 },
|
||||
"Size:"
|
||||
};
|
||||
Text text_sizestr {
|
||||
{ 8 * 8, 5 * 16, 16 * 8, 16 },
|
||||
"..."
|
||||
};
|
||||
Text text_md5 {
|
||||
{ 2 * 8, 6 * 16, 4 * 8, 16 },
|
||||
"MD5:"
|
||||
};
|
||||
Text text_md5_a {
|
||||
{ 7 * 8, 6 * 16, 16 * 8, 16 },
|
||||
"..."
|
||||
};
|
||||
Text text_md5_b {
|
||||
{ 7 * 8, 7 * 16, 16 * 8, 16 },
|
||||
"..."
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 4 * 8, 272, 64, 24 },
|
||||
"Ok"
|
||||
};
|
||||
};
|
||||
|
||||
class SetupMenuView : public MenuView {
|
||||
public:
|
||||
SetupMenuView(NavigationView& nav);
|
||||
|
@ -25,11 +25,13 @@
|
||||
#include "ch.h"
|
||||
#include "evtimer.h"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
#include "ff.h"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "radio.hpp"
|
||||
//#include "fox_bmp.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "hackrf_hal.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
@ -38,7 +40,7 @@
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace hackrf::one;
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -98,20 +100,23 @@ void SIGFRXView::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
}
|
||||
|
||||
void SIGFRXView::on_show() {
|
||||
context().message_map().register_handler(Message::ID::ChannelSpectrum,
|
||||
/*EventDispatcher::message_map().register_handler(Message::ID::ChannelSpectrum,
|
||||
[this](const Message* const p) {
|
||||
this->on_channel_spectrum(reinterpret_cast<const ChannelSpectrumMessage*>(p)->spectrum);
|
||||
}
|
||||
);
|
||||
);*/
|
||||
}
|
||||
|
||||
void SIGFRXView::on_hide() {
|
||||
//EventDispatcher::message_map().unregister_handler(Message::ID::ChannelSpectrum);
|
||||
}
|
||||
|
||||
SIGFRXView::SIGFRXView(
|
||||
NavigationView& nav,
|
||||
ReceiverModel& receiver_model
|
||||
) : receiver_model(receiver_model)
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
receiver_model.set_baseband_configuration({
|
||||
.mode = RX_SIGFOX,
|
||||
.mode = 255, // DEBUG
|
||||
.sampling_rate = 3072000,
|
||||
.decimation_factor = 4,
|
||||
});
|
||||
|
@ -36,17 +36,16 @@ namespace ui {
|
||||
|
||||
class SIGFRXView : public View {
|
||||
public:
|
||||
SIGFRXView(NavigationView& nav, ReceiverModel& receiver_model);
|
||||
SIGFRXView(NavigationView& nav);
|
||||
~SIGFRXView();
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
void focus() override;
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
ReceiverModel& receiver_model;
|
||||
|
||||
private:
|
||||
uint8_t last_channel;
|
||||
uint8_t detect_counter = 0;
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include "ch.h"
|
||||
#include "hackrf_hal.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
#include "ui_alphanum.hpp"
|
||||
#include "ff.h"
|
||||
#include "hackrf_gpio.hpp"
|
||||
@ -38,7 +39,7 @@
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
|
||||
using namespace hackrf::one;
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -91,9 +92,8 @@ void XylosRXView::on_show() {
|
||||
}
|
||||
|
||||
XylosRXView::XylosRXView(
|
||||
NavigationView& nav,
|
||||
ReceiverModel& receiver_model
|
||||
) : receiver_model(receiver_model)
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
char ccirdebug[21] = { 0,0,0,0,1,8,1,10,10,10,11,1,1,2,0,11,0,0,0,0,0xFF };
|
||||
|
||||
@ -211,9 +211,8 @@ void XylosView::journuit() {
|
||||
}
|
||||
|
||||
XylosView::XylosView(
|
||||
NavigationView& nav,
|
||||
TransmitterModel& transmitter_model
|
||||
) : transmitter_model(transmitter_model)
|
||||
NavigationView& nav
|
||||
)
|
||||
{
|
||||
static constexpr Style style_val {
|
||||
.font = font::fixed_8x16,
|
||||
@ -228,13 +227,11 @@ XylosView::XylosView(
|
||||
};
|
||||
|
||||
transmitter_model.set_baseband_configuration({
|
||||
.mode = TX_XYLOS,
|
||||
.mode = 4,
|
||||
.sampling_rate = 1536000,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
|
||||
transmitter_model.set_modulation(TX_XYLOS); // Useless ?
|
||||
|
||||
|
||||
add_children({ {
|
||||
&text_title,
|
||||
&button_txtest,
|
||||
@ -323,11 +320,9 @@ XylosView::XylosView(
|
||||
button_txtest.on_select = [this,&transmitter_model](Button&) {
|
||||
const uint8_t ccirtest[21] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,14,13,12,11,0xFF };
|
||||
if (txing == false) {
|
||||
auto& message_map = context().message_map();
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::TXDone);
|
||||
|
||||
message_map.unregister_handler(Message::ID::TXDone);
|
||||
|
||||
message_map.register_handler(Message::ID::TXDone,
|
||||
EventDispatcher::message_map().register_handler(Message::ID::TXDone,
|
||||
[this,&transmitter_model](Message* const p) {
|
||||
const auto message = static_cast<const TXDoneMessage*>(p);
|
||||
if (message->n == 25) {
|
||||
@ -359,11 +354,9 @@ XylosView::XylosView(
|
||||
if (txing == false) {
|
||||
upd_message();
|
||||
|
||||
auto& message_map = context().message_map();
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::TXDone);
|
||||
|
||||
message_map.unregister_handler(Message::ID::TXDone);
|
||||
|
||||
message_map.register_handler(Message::ID::TXDone,
|
||||
EventDispatcher::message_map().register_handler(Message::ID::TXDone,
|
||||
[this,&transmitter_model](Message* const p) {
|
||||
uint8_t c;
|
||||
char progress[21];
|
||||
|
@ -45,7 +45,7 @@ void do_something();
|
||||
|
||||
class XylosRXView : public View {
|
||||
public:
|
||||
XylosRXView(NavigationView& nav, ReceiverModel& receiver_model);
|
||||
XylosRXView(NavigationView& nav);
|
||||
~XylosRXView();
|
||||
|
||||
void talk();
|
||||
@ -91,8 +91,6 @@ private:
|
||||
"trailer.wav"
|
||||
};
|
||||
char ccir_received[21];
|
||||
|
||||
ReceiverModel& receiver_model;
|
||||
|
||||
Text text_title {
|
||||
{ 1 * 8, 1 * 16, 11, 16 },
|
||||
@ -142,7 +140,7 @@ private:
|
||||
|
||||
class XylosView : public View {
|
||||
public:
|
||||
XylosView(NavigationView& nav, TransmitterModel& transmitter_model);
|
||||
XylosView(NavigationView& nav);
|
||||
~XylosView();
|
||||
void journuit();
|
||||
|
||||
@ -184,8 +182,6 @@ private:
|
||||
};
|
||||
char ccirmessage[21];
|
||||
char ccir_received[21];
|
||||
|
||||
TransmitterModel& transmitter_model;
|
||||
|
||||
Text text_title {
|
||||
{ 1 * 8, 1 * 16, 11, 16 },
|
||||
|
Binary file not shown.
@ -124,16 +124,20 @@ CSRC = $(PORTSRC) \
|
||||
# setting.
|
||||
CPPSRC = main.cpp \
|
||||
message_queue.cpp \
|
||||
event.cpp \
|
||||
event_m4.cpp \
|
||||
irq_ipc_m4.cpp \
|
||||
gpdma.cpp \
|
||||
baseband_dma.cpp \
|
||||
baseband_sgpio.cpp \
|
||||
portapack_shared_memory.cpp \
|
||||
baseband_thread.cpp \
|
||||
baseband_processor.cpp \
|
||||
channel_decimator.cpp \
|
||||
baseband_stats_collector.cpp \
|
||||
dsp_decimate.cpp \
|
||||
dsp_demodulate.cpp \
|
||||
matched_filter.cpp \
|
||||
spectrum_collector.cpp \
|
||||
proc_rds.cpp \
|
||||
proc_jammer.cpp \
|
||||
proc_fsk_lcr.cpp \
|
||||
proc_xylos.cpp \
|
||||
@ -144,11 +148,15 @@ CPPSRC = main.cpp \
|
||||
packet_builder.cpp \
|
||||
dsp_fft.cpp \
|
||||
dsp_fir_taps.cpp \
|
||||
dsp_iir.cpp \
|
||||
fxpt_atan2.cpp \
|
||||
rssi.cpp \
|
||||
rssi_dma.cpp \
|
||||
rssi_thread.cpp \
|
||||
audio.cpp \
|
||||
audio_output.cpp \
|
||||
audio_dma.cpp \
|
||||
audio_stats_collector.cpp \
|
||||
touch_dma.cpp \
|
||||
../common/utility.cpp \
|
||||
../common/chibios_cpp.cpp \
|
||||
|
@ -121,7 +121,7 @@ constexpr gpdma::channel::Config config_rx() {
|
||||
|
||||
/* TODO: Clean up terminology around "buffer", "transfer", "samples" */
|
||||
|
||||
constexpr size_t buffer_samples_log2n = 8; // Bumped to 8, to allow filling at 750Hz
|
||||
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);
|
||||
@ -208,8 +208,8 @@ void enable() {
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel_i2s0_tx.disable_force();
|
||||
gpdma_channel_i2s0_rx.disable_force();
|
||||
gpdma_channel_i2s0_tx.disable();
|
||||
gpdma_channel_i2s0_rx.disable();
|
||||
}
|
||||
|
||||
buffer_t tx_empty_buffer() {
|
||||
|
100
firmware/baseband-tx/audio_output.cpp
Normal file
100
firmware/baseband-tx/audio_output.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "audio_output.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
|
||||
void AudioOutput::configure(
|
||||
const iir_biquad_config_t& hpf_config,
|
||||
const iir_biquad_config_t& deemph_config,
|
||||
const float squelch_threshold
|
||||
) {
|
||||
hpf.configure(hpf_config);
|
||||
deemph.configure(deemph_config);
|
||||
squelch.set_threshold(squelch_threshold);
|
||||
}
|
||||
|
||||
void AudioOutput::write(
|
||||
const buffer_s16_t& audio
|
||||
) {
|
||||
std::array<float, 32> audio_f;
|
||||
for(size_t i=0; i<audio.count; i++) {
|
||||
audio_f[i] = audio.p[i];
|
||||
}
|
||||
write(buffer_f32_t {
|
||||
audio_f.data(),
|
||||
audio.count,
|
||||
audio.sampling_rate
|
||||
});
|
||||
}
|
||||
|
||||
void AudioOutput::write(
|
||||
const buffer_f32_t& audio
|
||||
) {
|
||||
const auto audio_present_now = squelch.execute(audio);
|
||||
|
||||
hpf.execute_in_place(audio);
|
||||
deemph.execute_in_place(audio);
|
||||
|
||||
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
|
||||
const bool audio_present = (audio_present_history != 0);
|
||||
|
||||
if( audio_present ) {
|
||||
i2s::i2s0::tx_unmute();
|
||||
} else {
|
||||
i2s::i2s0::tx_mute();
|
||||
for(size_t i=0; i<audio.count; i++) {
|
||||
audio.p[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fill_audio_buffer(audio);
|
||||
}
|
||||
|
||||
void AudioOutput::fill_audio_buffer(const buffer_f32_t& audio) {
|
||||
auto audio_buffer = audio::dma::tx_empty_buffer();
|
||||
for(size_t i=0; i<audio_buffer.count; i++) {
|
||||
const int32_t sample_int = audio.p[i];
|
||||
const int32_t sample_saturated = __SSAT(sample_int, 16);
|
||||
audio_buffer.p[i].left = audio_buffer.p[i].right = sample_saturated;
|
||||
}
|
||||
|
||||
feed_audio_stats(audio);
|
||||
}
|
||||
|
||||
void AudioOutput::feed_audio_stats(const buffer_f32_t& audio) {
|
||||
audio_stats.feed(
|
||||
audio,
|
||||
[](const AudioStatistics& statistics) {
|
||||
const AudioStatisticsMessage audio_stats_message { statistics };
|
||||
shared_memory.application_queue.push(audio_stats_message);
|
||||
}
|
||||
);
|
||||
}
|
58
firmware/baseband-tx/audio_output.hpp
Normal file
58
firmware/baseband-tx/audio_output.hpp
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __AUDIO_OUTPUT_H__
|
||||
#define __AUDIO_OUTPUT_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_squelch.hpp"
|
||||
|
||||
#include "audio_stats_collector.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class AudioOutput {
|
||||
public:
|
||||
void configure(
|
||||
const iir_biquad_config_t& hpf_config,
|
||||
const iir_biquad_config_t& deemph_config = iir_config_passthrough,
|
||||
const float squelch_threshold = 0.0f
|
||||
);
|
||||
|
||||
void write(const buffer_s16_t& audio);
|
||||
void write(const buffer_f32_t& audio);
|
||||
|
||||
private:
|
||||
IIRBiquadFilter hpf;
|
||||
IIRBiquadFilter deemph;
|
||||
FMSquelch squelch;
|
||||
|
||||
AudioStatsCollector audio_stats;
|
||||
|
||||
uint64_t audio_present_history = 0;
|
||||
|
||||
void fill_audio_buffer(const buffer_f32_t& audio);
|
||||
void feed_audio_stats(const buffer_f32_t& audio);
|
||||
};
|
||||
|
||||
#endif/*__AUDIO_OUTPUT_H__*/
|
67
firmware/baseband-tx/audio_stats_collector.cpp
Normal file
67
firmware/baseband-tx/audio_stats_collector.cpp
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "audio_stats_collector.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
void AudioStatsCollector::consume_audio_buffer(const buffer_f32_t& src) {
|
||||
auto src_p = src.p;
|
||||
const auto src_end = &src.p[src.count];
|
||||
while(src_p < src_end) {
|
||||
const auto sample = *(src_p++);
|
||||
const auto sample_squared = sample * sample;
|
||||
squared_sum += sample_squared;
|
||||
if( sample_squared > max_squared ) {
|
||||
max_squared = sample_squared;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioStatsCollector::update_stats(const size_t sample_count, const size_t sampling_rate) {
|
||||
count += sample_count;
|
||||
|
||||
const size_t samples_per_update = sampling_rate * update_interval;
|
||||
|
||||
if( count >= samples_per_update ) {
|
||||
statistics.rms_db = complex16_mag_squared_to_dbv_norm(squared_sum / count);
|
||||
statistics.max_db = complex16_mag_squared_to_dbv_norm(max_squared);
|
||||
statistics.count = count;
|
||||
|
||||
squared_sum = 0;
|
||||
max_squared = 0;
|
||||
count = 0;
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AudioStatsCollector::feed(const buffer_f32_t& src) {
|
||||
consume_audio_buffer(src);
|
||||
|
||||
return update_stats(src.count, src.sampling_rate);
|
||||
}
|
||||
|
||||
bool AudioStatsCollector::mute(const size_t sample_count, const size_t sampling_rate) {
|
||||
return update_stats(sample_count, sampling_rate);
|
||||
}
|
@ -22,9 +22,8 @@
|
||||
#ifndef __AUDIO_STATS_COLLECTOR_H__
|
||||
#define __AUDIO_STATS_COLLECTOR_H__
|
||||
|
||||
#include "buffer.hpp"
|
||||
#include "dsp_types.hpp"
|
||||
#include "message.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
@ -32,64 +31,33 @@
|
||||
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) ) {
|
||||
void feed(const buffer_f32_t& src, Callback callback) {
|
||||
if( feed(src) ) {
|
||||
callback(statistics);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Callback>
|
||||
void mute(const size_t sample_count, const size_t sampling_rate, Callback callback) {
|
||||
if( update_stats(sample_count, sampling_rate) ) {
|
||||
if( mute(sample_count, sampling_rate) ) {
|
||||
callback(statistics);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr float update_interval { 0.1f };
|
||||
uint64_t squared_sum { 0 };
|
||||
uint32_t max_squared { 0 };
|
||||
float squared_sum { 0 };
|
||||
float 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
void consume_audio_buffer(const buffer_f32_t& src);
|
||||
|
||||
bool update_stats(const size_t sample_count, const size_t sampling_rate) {
|
||||
count += sample_count;
|
||||
bool update_stats(const size_t sample_count, const size_t sampling_rate);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
bool feed(const buffer_f32_t& src);
|
||||
bool mute(const size_t sample_count, const size_t sampling_rate);
|
||||
};
|
||||
|
||||
#endif/*__AUDIO_STATS_COLLECTOR_H__*/
|
||||
|
@ -20,7 +20,6 @@
|
||||
*/
|
||||
|
||||
#include "baseband_dma.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
@ -35,8 +34,6 @@ using namespace lpc43xx;
|
||||
|
||||
namespace baseband {
|
||||
namespace dma {
|
||||
|
||||
int quitt = 0;
|
||||
|
||||
constexpr uint32_t gpdma_ahb_master_sgpio = 0;
|
||||
constexpr uint32_t gpdma_ahb_master_memory = 1;
|
||||
@ -102,17 +99,12 @@ 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() {
|
||||
static 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);
|
||||
}
|
||||
|
||||
@ -121,7 +113,6 @@ static void dma_error() {
|
||||
}
|
||||
|
||||
void init() {
|
||||
//chMBInit(&mailbox, messages.data(), messages.size());
|
||||
chSemInit(&semaphore, 0);
|
||||
gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error);
|
||||
|
||||
@ -148,7 +139,6 @@ 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();
|
||||
@ -159,14 +149,11 @@ bool is_enabled() {
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel_sgpio.disable_force();
|
||||
gpdma_channel_sgpio.disable();
|
||||
}
|
||||
|
||||
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 ) {
|
||||
@ -174,28 +161,10 @@ baseband::buffer_t wait_for_rx_buffer() {
|
||||
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 };
|
||||
return { };
|
||||
}
|
||||
} 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 };
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,104 +23,14 @@
|
||||
|
||||
#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) {
|
||||
void BasebandProcessor::feed_channel_stats(const buffer_c16_t& channel) {
|
||||
channel_stats.feed(
|
||||
channel,
|
||||
[this](const ChannelStatistics statistics) {
|
||||
this->post_channel_stats_message(statistics);
|
||||
[](const ChannelStatistics& statistics) {
|
||||
const ChannelStatisticsMessage channel_stats_message { statistics };
|
||||
shared_memory.application_queue.push(channel_stats_message);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void BasebandProcessor::fill_buffer(int8_t * inptr) {
|
||||
(void)inptr;
|
||||
}
|
||||
|
@ -23,55 +23,24 @@
|
||||
#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>
|
||||
#include "message.hpp"
|
||||
|
||||
class BasebandProcessor {
|
||||
public:
|
||||
virtual ~BasebandProcessor() = default;
|
||||
|
||||
virtual void execute(buffer_c8_t buffer) = 0;
|
||||
virtual void fill_buffer(int8_t * inptr);
|
||||
virtual void execute(const buffer_c8_t& buffer) = 0;
|
||||
|
||||
void update_spectrum();
|
||||
virtual void on_message(const Message* const) { };
|
||||
|
||||
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 };
|
||||
void feed_channel_stats(const buffer_c16_t& channel);
|
||||
|
||||
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__*/
|
||||
|
59
firmware/baseband-tx/baseband_stats_collector.cpp
Normal file
59
firmware/baseband-tx/baseband_stats_collector.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "baseband_stats_collector.hpp"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
|
||||
bool BasebandStatsCollector::process(const buffer_c8_t& buffer) {
|
||||
samples += buffer.count;
|
||||
|
||||
const size_t report_samples = buffer.sampling_rate * report_interval;
|
||||
const auto report_delta = samples - samples_last_report;
|
||||
return report_delta >= report_samples;
|
||||
}
|
||||
|
||||
BasebandStatistics BasebandStatsCollector::capture_statistics() {
|
||||
BasebandStatistics statistics;
|
||||
|
||||
const auto idle_ticks = thread_idle->total_ticks;
|
||||
statistics.idle_ticks = (idle_ticks - last_idle_ticks);
|
||||
last_idle_ticks = idle_ticks;
|
||||
|
||||
const auto main_ticks = thread_main->total_ticks;
|
||||
statistics.main_ticks = (main_ticks - last_main_ticks);
|
||||
last_main_ticks = main_ticks;
|
||||
|
||||
const auto rssi_ticks = thread_rssi->total_ticks;
|
||||
statistics.rssi_ticks = (rssi_ticks - last_rssi_ticks);
|
||||
last_rssi_ticks = rssi_ticks;
|
||||
|
||||
const auto baseband_ticks = thread_baseband->total_ticks;
|
||||
statistics.baseband_ticks = (baseband_ticks - last_baseband_ticks);
|
||||
last_baseband_ticks = baseband_ticks;
|
||||
|
||||
statistics.saturation = lpc43xx::m4::flag_saturation();
|
||||
lpc43xx::m4::clear_flag_saturation();
|
||||
|
||||
samples_last_report = samples;
|
||||
|
||||
return statistics;
|
||||
}
|
@ -26,7 +26,6 @@
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "message.hpp"
|
||||
#include "utility_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
@ -46,36 +45,9 @@ public:
|
||||
}
|
||||
|
||||
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;
|
||||
void process(const buffer_c8_t& buffer, Callback callback) {
|
||||
if( process(buffer) ) {
|
||||
callback(capture_statistics());
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,6 +63,9 @@ private:
|
||||
uint32_t last_rssi_ticks { 0 };
|
||||
const Thread* const thread_baseband;
|
||||
uint32_t last_baseband_ticks { 0 };
|
||||
|
||||
bool process(const buffer_c8_t& buffer);
|
||||
BasebandStatistics capture_statistics();
|
||||
};
|
||||
|
||||
#endif/*__BASEBAND_STATS_COLLECTOR_H__*/
|
||||
|
155
firmware/baseband-tx/baseband_thread.cpp
Normal file
155
firmware/baseband-tx/baseband_thread.cpp
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "baseband_thread.hpp"
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
#include "baseband.hpp"
|
||||
#include "baseband_stats_collector.hpp"
|
||||
#include "baseband_sgpio.hpp"
|
||||
#include "baseband_dma.hpp"
|
||||
|
||||
#include "rssi.hpp"
|
||||
#include "i2s.hpp"
|
||||
|
||||
#include "proc_xylos.hpp"
|
||||
#include "proc_fsk_lcr.hpp"
|
||||
#include "proc_jammer.hpp"
|
||||
#include "proc_rds.hpp"
|
||||
#include "proc_playaudio.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
static baseband::SGPIO baseband_sgpio;
|
||||
|
||||
WORKING_AREA(baseband_thread_wa, 4096);
|
||||
|
||||
Thread* BasebandThread::start(const tprio_t priority) {
|
||||
return chThdCreateStatic(baseband_thread_wa, sizeof(baseband_thread_wa),
|
||||
priority, ThreadBase::fn,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
void BasebandThread::set_configuration(const BasebandConfiguration& new_configuration) {
|
||||
if( new_configuration.mode != baseband_configuration.mode ) {
|
||||
disable();
|
||||
|
||||
// TODO: Timing problem around disabling DMA and nulling and deleting old processor
|
||||
auto old_p = baseband_processor;
|
||||
baseband_processor = nullptr;
|
||||
delete old_p;
|
||||
|
||||
baseband_processor = create_processor(new_configuration.mode);
|
||||
|
||||
enable();
|
||||
}
|
||||
|
||||
baseband_configuration = new_configuration;
|
||||
}
|
||||
|
||||
void BasebandThread::on_message(const Message* const message) {
|
||||
if( message->id == Message::ID::BasebandConfiguration ) {
|
||||
set_configuration(reinterpret_cast<const BasebandConfigurationMessage*>(message)->configuration);
|
||||
} else {
|
||||
if( baseband_processor ) {
|
||||
baseband_processor->on_message(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BasebandThread::run() {
|
||||
baseband_sgpio.init();
|
||||
baseband::dma::init();
|
||||
|
||||
const auto baseband_buffer = new std::array<baseband::sample_t, 8192>();
|
||||
baseband::dma::configure(
|
||||
baseband_buffer->data(),
|
||||
direction()
|
||||
);
|
||||
//baseband::dma::allocate(4, 2048);
|
||||
|
||||
BasebandStatsCollector stats {
|
||||
chSysGetIdleThread(),
|
||||
thread_main,
|
||||
thread_rssi,
|
||||
chThdSelf()
|
||||
};
|
||||
|
||||
while(true) {
|
||||
// TODO: Place correct sampling rate into buffer returned here:
|
||||
const auto buffer_tmp = baseband::dma::wait_for_rx_buffer();
|
||||
if( buffer_tmp ) {
|
||||
buffer_c8_t buffer {
|
||||
buffer_tmp.p, buffer_tmp.count, baseband_configuration.sampling_rate
|
||||
};
|
||||
|
||||
if( baseband_processor ) {
|
||||
baseband_processor->execute(buffer);
|
||||
}
|
||||
|
||||
stats.process(buffer,
|
||||
[](const BasebandStatistics& statistics) {
|
||||
const BasebandStatisticsMessage message { statistics };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
delete baseband_buffer;
|
||||
}
|
||||
|
||||
BasebandProcessor* BasebandThread::create_processor(const int32_t mode) {
|
||||
switch(mode) {
|
||||
case 0: return new RDSProcessor();
|
||||
case 1: return new LCRFSKProcessor();
|
||||
case 2: return nullptr; //new ToneProcessor();
|
||||
case 3: return new JammerProcessor();
|
||||
case 4: return new XylosProcessor();
|
||||
case 5: return new PlayAudioProcessor();
|
||||
case 6: return nullptr; //new AFSKRXProcessor();
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void BasebandThread::disable() {
|
||||
if( baseband_processor ) {
|
||||
i2s::i2s0::tx_mute();
|
||||
baseband::dma::disable();
|
||||
baseband_sgpio.streaming_disable();
|
||||
rf::rssi::stop();
|
||||
}
|
||||
}
|
||||
|
||||
void BasebandThread::enable() {
|
||||
if( baseband_processor ) {
|
||||
if( direction() == baseband::Direction::Receive ) {
|
||||
rf::rssi::start();
|
||||
}
|
||||
baseband_sgpio.configure(direction());
|
||||
baseband::dma::enable(direction());
|
||||
baseband_sgpio.streaming_enable();
|
||||
}
|
||||
}
|
65
firmware/baseband-tx/baseband_thread.hpp
Normal file
65
firmware/baseband-tx/baseband_thread.hpp
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __BASEBAND_THREAD_H__
|
||||
#define __BASEBAND_THREAD_H__
|
||||
|
||||
#include "thread_base.hpp"
|
||||
#include "message.hpp"
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include <ch.h>
|
||||
|
||||
class BasebandThread : public ThreadBase {
|
||||
public:
|
||||
BasebandThread(
|
||||
) : ThreadBase { "baseband" }
|
||||
{
|
||||
}
|
||||
|
||||
Thread* start(const tprio_t priority);
|
||||
|
||||
void on_message(const Message* const message);
|
||||
|
||||
// This getter should die, it's just here to leak information to code that
|
||||
// isn't in the right place to begin with.
|
||||
baseband::Direction direction() const {
|
||||
return baseband::Direction::Receive;
|
||||
}
|
||||
|
||||
Thread* thread_main { nullptr };
|
||||
Thread* thread_rssi { nullptr };
|
||||
BasebandProcessor* baseband_processor { nullptr };
|
||||
|
||||
private:
|
||||
BasebandConfiguration baseband_configuration;
|
||||
|
||||
void run() override;
|
||||
|
||||
BasebandProcessor* create_processor(const int32_t mode);
|
||||
|
||||
void disable();
|
||||
void enable();
|
||||
|
||||
void set_configuration(const BasebandConfiguration& new_configuration);
|
||||
};
|
||||
|
||||
#endif/*__BASEBAND_THREAD_H__*/
|
@ -1 +1 @@
|
||||
More specific stuff for testing :o
|
||||
More or less experimental stuff, mainly TX.
|
||||
|
57
firmware/baseband-tx/dsp_iir.cpp
Normal file
57
firmware/baseband-tx/dsp_iir.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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_iir.hpp"
|
||||
|
||||
#include <hal.h>
|
||||
|
||||
void IIRBiquadFilter::configure(const iir_biquad_config_t& new_config) {
|
||||
config = new_config;
|
||||
}
|
||||
|
||||
void IIRBiquadFilter::execute(const buffer_f32_t& buffer_in, const buffer_f32_t& buffer_out) {
|
||||
const auto a_ = config.a;
|
||||
const auto b_ = config.b;
|
||||
|
||||
auto x_ = x;
|
||||
auto y_ = y;
|
||||
|
||||
// TODO: Assert that buffer_out.count == buffer_in.count.
|
||||
for(size_t i=0; i<buffer_out.count; i++) {
|
||||
x_[0] = x_[1];
|
||||
x_[1] = x_[2];
|
||||
x_[2] = buffer_in.p[i];
|
||||
|
||||
y_[0] = y_[1];
|
||||
y_[1] = y_[2];
|
||||
y_[2] = b_[0] * x_[2] + b_[1] * x_[1] + b_[2] * x_[0]
|
||||
- a_[1] * y_[1] - a_[2] * y_[0];
|
||||
|
||||
buffer_out.p[i] = y_[2];
|
||||
}
|
||||
|
||||
x = x_;
|
||||
y = y_;
|
||||
}
|
||||
|
||||
void IIRBiquadFilter::execute_in_place(const buffer_f32_t& buffer) {
|
||||
execute(buffer, buffer);
|
||||
}
|
@ -27,13 +27,27 @@
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
struct iir_biquad_config_t {
|
||||
const std::array<float, 3> b;
|
||||
const std::array<float, 3> a;
|
||||
std::array<float, 3> b;
|
||||
std::array<float, 3> a;
|
||||
};
|
||||
|
||||
constexpr iir_biquad_config_t iir_config_passthrough {
|
||||
{ { 1.0f, 0.0f, 0.0f } },
|
||||
{ { 0.0f, 0.0f, 0.0f } },
|
||||
};
|
||||
|
||||
constexpr iir_biquad_config_t iir_config_no_pass {
|
||||
{ { 0.0f, 0.0f, 0.0f } },
|
||||
{ { 0.0f, 0.0f, 0.0f } },
|
||||
};
|
||||
|
||||
class IIRBiquadFilter {
|
||||
public:
|
||||
// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
||||
constexpr IIRBiquadFilter(
|
||||
) : IIRBiquadFilter(iir_config_no_pass)
|
||||
{
|
||||
}
|
||||
|
||||
// Assume all coefficients are normalized so that a0=1.0
|
||||
constexpr IIRBiquadFilter(
|
||||
@ -42,34 +56,15 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
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 configure(const iir_biquad_config_t& new_config);
|
||||
|
||||
void execute_in_place(buffer_s16_t buffer) {
|
||||
execute(buffer, buffer);
|
||||
}
|
||||
void execute(const buffer_f32_t& buffer_in, const buffer_f32_t& buffer_out);
|
||||
void execute_in_place(const buffer_f32_t& buffer);
|
||||
|
||||
private:
|
||||
const iir_biquad_config_t config;
|
||||
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__*/
|
||||
|
@ -24,22 +24,30 @@
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
bool FMSquelch::execute(buffer_s16_t audio) {
|
||||
bool FMSquelch::execute(const buffer_f32_t& audio) {
|
||||
if( threshold_squared == 0.0f ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: No hard-coded array size.
|
||||
std::array<int16_t, N> squelch_energy_buffer;
|
||||
const buffer_s16_t squelch_energy {
|
||||
std::array<float, N> squelch_energy_buffer;
|
||||
const buffer_f32_t squelch_energy {
|
||||
squelch_energy_buffer.data(),
|
||||
squelch_energy_buffer.size()
|
||||
};
|
||||
non_audio_hpf.execute(audio, squelch_energy);
|
||||
|
||||
uint64_t max_squared = 0;
|
||||
float non_audio_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;
|
||||
const float sample_squared = sample * sample;
|
||||
if( sample_squared > non_audio_max_squared ) {
|
||||
non_audio_max_squared = sample_squared;
|
||||
}
|
||||
}
|
||||
|
||||
return (max_squared < (threshold * threshold));
|
||||
return (non_audio_max_squared < threshold_squared);
|
||||
}
|
||||
|
||||
void FMSquelch::set_threshold(const float new_value) {
|
||||
threshold_squared = new_value * new_value;
|
||||
}
|
||||
|
@ -31,14 +31,14 @@
|
||||
|
||||
class FMSquelch {
|
||||
public:
|
||||
bool execute(buffer_s16_t audio);
|
||||
bool execute(const buffer_f32_t& audio);
|
||||
|
||||
void set_threshold(const float new_value);
|
||||
|
||||
private:
|
||||
static constexpr size_t N = 32;
|
||||
static constexpr int16_t threshold = 3072;
|
||||
float threshold_squared { 0.0f };
|
||||
|
||||
// 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 };
|
||||
};
|
||||
|
||||
|
@ -21,10 +21,99 @@
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "message_queue.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
Thread* thread_event_loop = nullptr;
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
void events_initialize(Thread* const event_loop_thread) {
|
||||
thread_event_loop = event_loop_thread;
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
extern "C" {
|
||||
|
||||
CH_IRQ_HANDLER(MAPP_IRQHandler) {
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
chSysLockFromIsr();
|
||||
EventDispatcher::events_flag_isr(EVT_MASK_BASEBAND);
|
||||
chSysUnlockFromIsr();
|
||||
|
||||
creg::m0apptxevent::clear();
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Thread* EventDispatcher::thread_event_loop = nullptr;
|
||||
|
||||
void EventDispatcher::run() {
|
||||
thread_event_loop = chThdSelf();
|
||||
lpc43xx::creg::m0apptxevent::enable();
|
||||
|
||||
baseband_thread.thread_main = chThdSelf();
|
||||
baseband_thread.thread_rssi = rssi_thread.start(NORMALPRIO + 10);
|
||||
baseband_thread.start(NORMALPRIO + 20);
|
||||
|
||||
while(is_running) {
|
||||
const auto events = wait();
|
||||
dispatch(events);
|
||||
}
|
||||
|
||||
lpc43xx::creg::m0apptxevent::disable();
|
||||
}
|
||||
|
||||
void EventDispatcher::request_stop() {
|
||||
is_running = false;
|
||||
}
|
||||
|
||||
eventmask_t EventDispatcher::wait() {
|
||||
return chEvtWaitAny(ALL_EVENTS);
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatch(const eventmask_t events) {
|
||||
if( events & EVT_MASK_BASEBAND ) {
|
||||
handle_baseband_queue();
|
||||
}
|
||||
|
||||
if( events & EVT_MASK_SPECTRUM ) {
|
||||
handle_spectrum();
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::handle_baseband_queue() {
|
||||
std::array<uint8_t, Message::MAX_SIZE> message_buffer;
|
||||
while(Message* const message = shared_memory.baseband_queue.peek(message_buffer)) {
|
||||
on_message(message);
|
||||
shared_memory.baseband_queue.skip();
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
case Message::ID::Shutdown:
|
||||
on_message_shutdown(*reinterpret_cast<const ShutdownMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
on_message_default(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::on_message_shutdown(const ShutdownMessage&) {
|
||||
request_stop();
|
||||
}
|
||||
|
||||
void EventDispatcher::on_message_default(const Message* const message) {
|
||||
baseband_thread.on_message(message);
|
||||
}
|
||||
|
||||
void EventDispatcher::handle_spectrum() {
|
||||
const UpdateSpectrumMessage message;
|
||||
baseband_thread.on_message(&message);
|
||||
}
|
||||
|
@ -22,25 +22,54 @@
|
||||
#ifndef __EVENT_M4_H__
|
||||
#define __EVENT_M4_H__
|
||||
|
||||
#include "event.hpp"
|
||||
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
constexpr auto EVT_MASK_BASEBAND = EVENT_MASK(0);
|
||||
constexpr auto EVT_MASK_SPECTRUM = EVENT_MASK(1);
|
||||
|
||||
void events_initialize(Thread* const event_loop_thread);
|
||||
class EventDispatcher {
|
||||
public:
|
||||
void run();
|
||||
void request_stop();
|
||||
|
||||
extern Thread* thread_event_loop;
|
||||
|
||||
inline void events_flag(const eventmask_t events) {
|
||||
if( thread_event_loop ) {
|
||||
chEvtSignal(thread_event_loop, events);
|
||||
static 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);
|
||||
static inline void events_flag_isr(const eventmask_t events) {
|
||||
if( thread_event_loop ) {
|
||||
chEvtSignalI(thread_event_loop, events);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static Thread* thread_event_loop;
|
||||
|
||||
BasebandThread baseband_thread;
|
||||
RSSIThread rssi_thread;
|
||||
|
||||
bool is_running = true;
|
||||
|
||||
eventmask_t wait();
|
||||
|
||||
void dispatch(const eventmask_t events);
|
||||
|
||||
void handle_baseband_queue();
|
||||
|
||||
void on_message(const Message* const message);
|
||||
void on_message_shutdown(const ShutdownMessage&);
|
||||
void on_message_default(const Message* const message);
|
||||
|
||||
void handle_spectrum();
|
||||
};
|
||||
|
||||
#endif/*__EVENT_M4_H__*/
|
||||
|
@ -15,13 +15,11 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* 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"
|
||||
|
||||
@ -30,34 +28,13 @@
|
||||
|
||||
#include "gpdma.hpp"
|
||||
|
||||
#include "baseband.hpp"
|
||||
#include "baseband_dma.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "irq_ipc_m4.hpp"
|
||||
|
||||
#include "touch_dma.hpp"
|
||||
|
||||
#include "modules.h"
|
||||
|
||||
#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_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
#include "baseband_processor.hpp"
|
||||
#include "proc_fsk_lcr.hpp"
|
||||
#include "proc_jammer.hpp"
|
||||
#include "proc_xylos.hpp"
|
||||
#include "proc_playaudio.hpp"
|
||||
|
||||
#include "clock_recovery.hpp"
|
||||
#include "packet_builder.hpp"
|
||||
|
||||
#include "message_queue.hpp"
|
||||
|
||||
@ -73,55 +50,87 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <bitset>
|
||||
#include <math.h>
|
||||
|
||||
static baseband::Direction direction = baseband::Direction::Receive;
|
||||
extern "C" {
|
||||
|
||||
class ThreadBase {
|
||||
public:
|
||||
constexpr ThreadBase(
|
||||
const char* const name
|
||||
) : name { name }
|
||||
{
|
||||
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 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));
|
||||
|
||||
touch::dma::init();
|
||||
touch::dma::allocate();
|
||||
touch::dma::enable();
|
||||
}
|
||||
|
||||
static void halt() {
|
||||
port_disable();
|
||||
while(true) {
|
||||
port_wait_for_interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
static msg_t fn(void* arg) {
|
||||
auto obj = static_cast<ThreadBase*>(arg);
|
||||
chRegSetThreadName(obj->name);
|
||||
obj->run();
|
||||
static void shutdown() {
|
||||
// TODO: Is this complete?
|
||||
|
||||
nvicDisableVector(DMA_IRQn);
|
||||
|
||||
chSysDisable();
|
||||
|
||||
return 0;
|
||||
}
|
||||
systick_stop();
|
||||
|
||||
virtual void run() = 0;
|
||||
ShutdownMessage shutdown_message;
|
||||
shared_memory.application_queue.push(shutdown_message);
|
||||
|
||||
private:
|
||||
const char* const name;
|
||||
};
|
||||
halt();
|
||||
}
|
||||
|
||||
class BasebandThread : public ThreadBase {
|
||||
public:
|
||||
BasebandThread(
|
||||
) : ThreadBase { "baseband" }
|
||||
{
|
||||
}
|
||||
int main(void) {
|
||||
init();
|
||||
|
||||
Thread* start(const tprio_t priority) {
|
||||
return chThdCreateStatic(wa, sizeof(wa),
|
||||
priority, ThreadBase::fn,
|
||||
this
|
||||
);
|
||||
}
|
||||
/* TODO: Ensure DMAs are configured to point at first LLI in chain. */
|
||||
|
||||
Thread* thread_main { nullptr };
|
||||
BasebandProcessor* baseband_processor { nullptr };
|
||||
BasebandConfiguration baseband_configuration;
|
||||
EventDispatcher event_dispatcher;
|
||||
event_dispatcher.run();
|
||||
|
||||
private:
|
||||
WORKING_AREA(wa, 2048);
|
||||
shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
void run() override {
|
||||
while(true) {
|
||||
if (direction == baseband::Direction::Transmit) {
|
||||
@ -149,167 +158,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
#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 {
|
||||
@ -348,130 +196,6 @@ private:
|
||||
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>();
|
||||
|
||||
char ram_loop[32];
|
||||
typedef int (*fn_ptr)(void);
|
||||
fn_ptr loop_ptr;
|
||||
@ -586,27 +310,4 @@ int main(void) {
|
||||
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;
|
||||
}
|
||||
*/
|
||||
|
@ -1 +1 @@
|
||||
Second module
|
||||
Experimental
|
||||
|
@ -26,6 +26,6 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void AudioTXProcessor::execute(buffer_c8_t buffer) {
|
||||
void AudioTXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
}
|
||||
|
@ -29,7 +29,8 @@
|
||||
|
||||
class AudioTXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
|
||||
private:
|
||||
int8_t audio_fifo[SAMPLERATE];
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void LCRFSKProcessor::execute(buffer_c8_t buffer) {
|
||||
void LCRFSKProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
for (size_t i = 0; i<buffer.count; i++) {
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
class LCRFSKProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
int8_t re, im;
|
||||
|
@ -27,33 +27,9 @@
|
||||
|
||||
#define POLY_MASK_32 0xB4BCD35C
|
||||
|
||||
|
||||
void JammerProcessor::execute(buffer_c8_t buffer) {
|
||||
void JammerProcessor::execute(const 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 (;;) {
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
class JammerProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
int32_t lfsr32 = 0xABCDE;
|
||||
|
@ -33,7 +33,7 @@ void PlayAudioProcessor::fill_buffer(int8_t * inptr) {
|
||||
asked = false;
|
||||
}
|
||||
|
||||
void PlayAudioProcessor::execute(buffer_c8_t buffer) {
|
||||
void PlayAudioProcessor::execute(const buffer_c8_t& buffer){
|
||||
|
||||
// This is called at 1536000/2048 = 750Hz
|
||||
|
||||
@ -69,5 +69,5 @@ void PlayAudioProcessor::execute(buffer_c8_t buffer) {
|
||||
buffer.p[i] = {(int8_t)re,(int8_t)im};
|
||||
}
|
||||
|
||||
fill_audio_buffer(preview_audio_buffer);
|
||||
//fill_audio_buffer(preview_audio_buffer);
|
||||
}
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
class PlayAudioProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
void fill_buffer(int8_t * inptr);
|
||||
|
||||
private:
|
||||
|
90
firmware/baseband-tx/proc_rds.cpp
Normal file
90
firmware/baseband-tx/proc_rds.cpp
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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_rds.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
void RDSProcessor::execute(const 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 >= 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};
|
||||
}
|
||||
}
|
||||
|
130
firmware/baseband-tx/proc_rds.hpp
Normal file
130
firmware/baseband-tx/proc_rds.hpp
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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_RDS_H__
|
||||
#define __PROC_RDS_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#define SAMPLES_PER_BIT 192
|
||||
#define FILTER_SIZE 576
|
||||
#define SAMPLE_BUFFER_SIZE SAMPLES_PER_BIT + FILTER_SIZE
|
||||
|
||||
class RDSProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
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;
|
||||
|
||||
int32_t waveform_biphase[576] = {
|
||||
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
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
@ -21,6 +21,10 @@
|
||||
*/
|
||||
|
||||
#include "proc_xylos.hpp"
|
||||
|
||||
#include "dsp_iir_config.hpp"
|
||||
//#include "audio_output.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table.hpp"
|
||||
|
||||
@ -31,7 +35,7 @@
|
||||
// 14 13 12 11:
|
||||
// 2108 989 2259 931
|
||||
|
||||
void XylosProcessor::execute(buffer_c8_t buffer) {
|
||||
void XylosProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
// This is called at 1536000/2048 = 750Hz
|
||||
|
||||
@ -88,5 +92,5 @@ void XylosProcessor::execute(buffer_c8_t buffer) {
|
||||
buffer.p[i] = {(int8_t)re,(int8_t)im};
|
||||
}
|
||||
|
||||
fill_audio_buffer(preview_audio_buffer);
|
||||
//audio_output.write(preview_audio_buffer);
|
||||
}
|
||||
|
@ -25,12 +25,18 @@
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
|
||||
//#include "audio_output.hpp"
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#define CCIR_TONELENGTH 15360-1 // 1536000/10/10
|
||||
#define PHASEV 436.91 // (65536*1024)/1536000*10
|
||||
|
||||
class XylosProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
int16_t audio_data[64];
|
||||
@ -67,6 +73,8 @@ private:
|
||||
uint32_t aphase, phase, sphase;
|
||||
int32_t sample, frq;
|
||||
TXDoneMessage message;
|
||||
|
||||
//AudioOutput audio_output;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -157,7 +157,7 @@ bool is_enabled() {
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel.disable_force();
|
||||
gpdma_channel.disable();
|
||||
}
|
||||
|
||||
rf::rssi::buffer_t wait_for_buffer() {
|
||||
|
63
firmware/baseband-tx/rssi_thread.cpp
Normal file
63
firmware/baseband-tx/rssi_thread.cpp
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "rssi_thread.hpp"
|
||||
|
||||
#include "rssi.hpp"
|
||||
#include "rssi_dma.hpp"
|
||||
#include "rssi_stats_collector.hpp"
|
||||
|
||||
#include "message.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
WORKING_AREA(rssi_thread_wa, 128);
|
||||
|
||||
Thread* RSSIThread::start(const tprio_t priority) {
|
||||
return chThdCreateStatic(rssi_thread_wa, sizeof(rssi_thread_wa),
|
||||
priority, ThreadBase::fn,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
void RSSIThread::run() {
|
||||
rf::rssi::init();
|
||||
rf::rssi::dma::allocate(4, 400);
|
||||
|
||||
RSSIStatisticsCollector stats;
|
||||
|
||||
while(true) {
|
||||
// TODO: Place correct sampling rate into buffer returned here:
|
||||
const auto buffer_tmp = rf::rssi::dma::wait_for_buffer();
|
||||
const rf::rssi::buffer_t buffer {
|
||||
buffer_tmp.p, buffer_tmp.count, sampling_rate
|
||||
};
|
||||
|
||||
stats.process(
|
||||
buffer,
|
||||
[](const RSSIStatistics& statistics) {
|
||||
const RSSIStatisticsMessage message { statistics };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
rf::rssi::dma::free();
|
||||
}
|
46
firmware/baseband-tx/rssi_thread.hpp
Normal file
46
firmware/baseband-tx/rssi_thread.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 __RSSI_THREAD_H__
|
||||
#define __RSSI_THREAD_H__
|
||||
|
||||
#include "thread_base.hpp"
|
||||
|
||||
#include <ch.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class RSSIThread : public ThreadBase {
|
||||
public:
|
||||
RSSIThread(
|
||||
) : ThreadBase { "rssi" }
|
||||
{
|
||||
}
|
||||
|
||||
Thread* start(const tprio_t priority);
|
||||
|
||||
private:
|
||||
void run() override;
|
||||
|
||||
const uint32_t sampling_rate { 400000 };
|
||||
};
|
||||
|
||||
#endif/*__RSSI_THREAD_H__*/
|
130
firmware/baseband-tx/spectrum_collector.cpp
Normal file
130
firmware/baseband-tx/spectrum_collector.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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 "spectrum_collector.hpp"
|
||||
|
||||
#include "dsp_fft.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
#include "event_m4.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
void SpectrumCollector::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
case Message::ID::UpdateSpectrum:
|
||||
update();
|
||||
break;
|
||||
|
||||
case Message::ID::SpectrumStreamingConfig:
|
||||
set_state(*reinterpret_cast<const SpectrumStreamingConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SpectrumCollector::set_state(const SpectrumStreamingConfigMessage& message) {
|
||||
if( message.mode == SpectrumStreamingConfigMessage::Mode::Running ) {
|
||||
start();
|
||||
} else {
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
void SpectrumCollector::start() {
|
||||
streaming = true;
|
||||
ChannelSpectrumConfigMessage message { &fifo };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
|
||||
void SpectrumCollector::stop() {
|
||||
streaming = false;
|
||||
fifo.reset_in();
|
||||
}
|
||||
|
||||
void SpectrumCollector::set_decimation_factor(
|
||||
const size_t decimation_factor
|
||||
) {
|
||||
channel_spectrum_decimator.set_factor(decimation_factor);
|
||||
}
|
||||
|
||||
/* TODO: Refactor to register task with idle thread?
|
||||
* It's sad that the idle thread has to call all the way back here just to
|
||||
* perform the deferred task on the buffer of data we prepared.
|
||||
*/
|
||||
|
||||
void SpectrumCollector::feed(
|
||||
const buffer_c16_t& channel,
|
||||
const uint32_t filter_pass_frequency,
|
||||
const uint32_t filter_stop_frequency
|
||||
) {
|
||||
// Called from baseband processing thread.
|
||||
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_message(data);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void SpectrumCollector::post_message(const buffer_c16_t& data) {
|
||||
// Called from baseband processing thread.
|
||||
if( streaming && !channel_spectrum_request_update ) {
|
||||
fft_swap(data, channel_spectrum);
|
||||
channel_spectrum_sampling_rate = data.sampling_rate;
|
||||
channel_spectrum_request_update = true;
|
||||
EventDispatcher::events_flag(EVT_MASK_SPECTRUM);
|
||||
}
|
||||
}
|
||||
|
||||
void SpectrumCollector::update() {
|
||||
// Called from idle thread (after EVT_MASK_SPECTRUM is flagged)
|
||||
if( streaming && channel_spectrum_request_update ) {
|
||||
/* Decimated buffer is full. Compute spectrum. */
|
||||
fft_c_preswapped(channel_spectrum);
|
||||
|
||||
ChannelSpectrum spectrum;
|
||||
spectrum.sampling_rate = channel_spectrum_sampling_rate;
|
||||
spectrum.channel_filter_pass_frequency = channel_filter_pass_frequency;
|
||||
spectrum.channel_filter_stop_frequency = channel_filter_stop_frequency;
|
||||
for(size_t i=0; i<spectrum.db.size(); i++) {
|
||||
// Three point Hamming window.
|
||||
const auto corrected_sample = channel_spectrum[i] * 0.54f
|
||||
+ (channel_spectrum[(i-1) & 0xff] + channel_spectrum[(i+1) & 0xff]) * -0.23f;
|
||||
const auto mag2 = magnitude_squared(corrected_sample);
|
||||
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.db[i] = std::max(0U, std::min(255U, v));
|
||||
}
|
||||
fifo.in(spectrum);
|
||||
}
|
||||
|
||||
channel_spectrum_request_update = false;
|
||||
}
|
72
firmware/baseband-tx/spectrum_collector.hpp
Normal file
72
firmware/baseband-tx/spectrum_collector.hpp
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* 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 __SPECTRUM_COLLECTOR_H__
|
||||
#define __SPECTRUM_COLLECTOR_H__
|
||||
|
||||
#include "dsp_types.hpp"
|
||||
#include "complex.hpp"
|
||||
|
||||
#include "block_decimator.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
class SpectrumCollector {
|
||||
public:
|
||||
constexpr SpectrumCollector(
|
||||
) : channel_spectrum_decimator { 1 }
|
||||
{
|
||||
}
|
||||
|
||||
void on_message(const Message* const message);
|
||||
|
||||
void set_decimation_factor(const size_t decimation_factor);
|
||||
|
||||
void feed(
|
||||
const buffer_c16_t& channel,
|
||||
const uint32_t filter_pass_frequency,
|
||||
const uint32_t filter_stop_frequency
|
||||
);
|
||||
|
||||
private:
|
||||
BlockDecimator<256> channel_spectrum_decimator;
|
||||
ChannelSpectrumFIFO fifo;
|
||||
|
||||
volatile bool channel_spectrum_request_update { false };
|
||||
bool streaming { 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 };
|
||||
|
||||
void post_message(const buffer_c16_t& data);
|
||||
|
||||
void set_state(const SpectrumStreamingConfigMessage& message);
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
void update();
|
||||
};
|
||||
|
||||
#endif/*__SPECTRUM_COLLECTOR_H__*/
|
50
firmware/baseband-tx/thread_base.hpp
Normal file
50
firmware/baseband-tx/thread_base.hpp
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __THREAD_BASE_H__
|
||||
#define __THREAD_BASE_H__
|
||||
|
||||
#include <ch.h>
|
||||
|
||||
class ThreadBase {
|
||||
public:
|
||||
constexpr ThreadBase(
|
||||
const char* const name
|
||||
) : name { name }
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
static msg_t fn(void* arg) {
|
||||
auto obj = static_cast<ThreadBase*>(arg);
|
||||
chRegSetThreadName(obj->name);
|
||||
obj->run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
const char* const name;
|
||||
|
||||
virtual void run() = 0;
|
||||
};
|
||||
|
||||
#endif/*__THREAD_BASE_H__*/
|
@ -122,7 +122,7 @@ bool is_enabled() {
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel.disable_force();
|
||||
gpdma_channel.disable();
|
||||
}
|
||||
|
||||
} /* namespace dma */
|
||||
|
Binary file not shown.
@ -1 +1 @@
|
||||
Basic RX/TX stuff for testing :)
|
||||
Original firmware's functionalities: Receiver.
|
||||
|
@ -50,121 +50,6 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <bitset>
|
||||
|
||||
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 };
|
||||
Thread* thread_rssi { nullptr };
|
||||
BasebandProcessor* baseband_processor { nullptr };
|
||||
BasebandConfiguration baseband_configuration;
|
||||
|
||||
private:
|
||||
WORKING_AREA(wa, 2048);
|
||||
|
||||
void run() override {
|
||||
BasebandStatsCollector stats {
|
||||
chSysGetIdleThread(),
|
||||
thread_main,
|
||||
thread_rssi,
|
||||
chThdSelf()
|
||||
};
|
||||
|
||||
while(true) {
|
||||
// TODO: Place correct sampling rate into buffer returned here:
|
||||
const auto buffer_tmp = baseband::dma::wait_for_rx_buffer();
|
||||
const buffer_c8_t buffer {
|
||||
buffer_tmp.p, buffer_tmp.count, baseband_configuration.sampling_rate
|
||||
};
|
||||
|
||||
if( baseband_processor ) {
|
||||
baseband_processor->execute(buffer);
|
||||
}
|
||||
|
||||
stats.process(buffer,
|
||||
[](const BasebandStatistics statistics) {
|
||||
const BasebandStatisticsMessage message { statistics };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class RSSIThread : public ThreadBase {
|
||||
public:
|
||||
RSSIThread(
|
||||
) : ThreadBase { "rssi" }
|
||||
{
|
||||
}
|
||||
|
||||
Thread* start(const tprio_t priority) {
|
||||
return chThdCreateStatic(wa, sizeof(wa),
|
||||
priority, ThreadBase::fn,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
uint32_t sampling_rate { 400000 };
|
||||
|
||||
private:
|
||||
WORKING_AREA(wa, 128);
|
||||
|
||||
void run() override {
|
||||
RSSIStatisticsCollector stats;
|
||||
|
||||
while(true) {
|
||||
// TODO: Place correct sampling rate into buffer returned here:
|
||||
const auto buffer_tmp = rf::rssi::dma::wait_for_buffer();
|
||||
const rf::rssi::buffer_t buffer {
|
||||
buffer_tmp.p, buffer_tmp.count, sampling_rate
|
||||
};
|
||||
|
||||
stats.process(
|
||||
buffer,
|
||||
[](const RSSIStatistics statistics) {
|
||||
const RSSIStatisticsMessage message { statistics };
|
||||
shared_memory.application_queue.push(message);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" {
|
||||
|
||||
@ -187,9 +72,6 @@ void __late_init(void) {
|
||||
|
||||
}
|
||||
|
||||
static BasebandThread baseband_thread;
|
||||
static RSSIThread rssi_thread;
|
||||
|
||||
static void init() {
|
||||
i2s::i2s0::configure(
|
||||
audio::i2s0_config_tx,
|
||||
@ -208,31 +90,9 @@ static void init() {
|
||||
gpdma::controller.enable();
|
||||
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));
|
||||
|
||||
baseband::dma::init();
|
||||
|
||||
rf::rssi::init();
|
||||
touch::dma::init();
|
||||
|
||||
const auto thread_main = chThdSelf();
|
||||
|
||||
const auto thread_rssi = rssi_thread.start(NORMALPRIO + 10);
|
||||
|
||||
baseband_thread.thread_main = thread_main;
|
||||
baseband_thread.thread_rssi = thread_rssi;
|
||||
|
||||
baseband_thread.start(NORMALPRIO + 20);
|
||||
}
|
||||
|
||||
static void shutdown() {
|
||||
// TODO: Is this complete?
|
||||
|
||||
nvicDisableVector(DMA_IRQn);
|
||||
|
||||
m0apptxevent_interrupt_disable();
|
||||
|
||||
chSysDisable();
|
||||
|
||||
systick_stop();
|
||||
touch::dma::allocate();
|
||||
touch::dma::enable();
|
||||
}
|
||||
|
||||
static void halt() {
|
||||
@ -242,146 +102,27 @@ static void halt() {
|
||||
}
|
||||
}
|
||||
|
||||
class EventDispatcher {
|
||||
public:
|
||||
MessageHandlerMap& message_handlers() {
|
||||
return message_map;
|
||||
}
|
||||
static void shutdown() {
|
||||
// TODO: Is this complete?
|
||||
|
||||
nvicDisableVector(DMA_IRQn);
|
||||
|
||||
chSysDisable();
|
||||
|
||||
void run() {
|
||||
while(is_running) {
|
||||
const auto events = wait();
|
||||
dispatch(events);
|
||||
}
|
||||
}
|
||||
systick_stop();
|
||||
|
||||
void request_stop() {
|
||||
is_running = false;
|
||||
}
|
||||
ShutdownMessage shutdown_message;
|
||||
shared_memory.application_queue.push(shutdown_message);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr auto direction = baseband::Direction::Receive;
|
||||
halt();
|
||||
}
|
||||
|
||||
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();
|
||||
rf::rssi::stop();
|
||||
}
|
||||
|
||||
// TODO: Timing problem around disabling DMA and nulling and deleting old processor
|
||||
auto old_p = baseband_thread.baseband_processor;
|
||||
baseband_thread.baseband_processor = nullptr;
|
||||
delete old_p;
|
||||
|
||||
switch(message->configuration.mode) {
|
||||
case 0:
|
||||
baseband_thread.baseband_processor = new NarrowbandAMAudio();
|
||||
break;
|
||||
|
||||
case 1:
|
||||
baseband_thread.baseband_processor = new NarrowbandFMAudio();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
baseband_thread.baseband_processor = new WidebandFMAudio();
|
||||
break;
|
||||
|
||||
case 3:
|
||||
baseband_thread.baseband_processor = new AISProcessor();
|
||||
break;
|
||||
|
||||
case 4:
|
||||
baseband_thread.baseband_processor = new WidebandSpectrum();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
baseband_thread.baseband_processor = new TPMSProcessor();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if( baseband_thread.baseband_processor ) {
|
||||
if( direction == baseband::Direction::Receive ) {
|
||||
rf::rssi::start();
|
||||
}
|
||||
baseband::dma::enable(direction);
|
||||
}
|
||||
}
|
||||
|
||||
baseband_thread.baseband_configuration = message->configuration;
|
||||
}
|
||||
);
|
||||
|
||||
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. */
|
||||
|
||||
if( direction == baseband::Direction::Receive ) {
|
||||
rf::rssi::dma::allocate(4, 400);
|
||||
}
|
||||
|
||||
touch::dma::allocate();
|
||||
touch::dma::enable();
|
||||
|
||||
const auto baseband_buffer =
|
||||
new std::array<baseband::sample_t, 8192>();
|
||||
baseband::dma::configure(
|
||||
baseband_buffer->data(),
|
||||
direction
|
||||
);
|
||||
|
||||
EventDispatcher event_dispatcher;
|
||||
event_dispatcher.run();
|
||||
|
||||
shutdown();
|
||||
|
@ -1 +1 @@
|
||||
First module
|
||||
Receiver
|
||||
|
@ -25,30 +25,17 @@
|
||||
|
||||
using namespace lpc43xx;
|
||||
|
||||
void AFSKRXProcessor::execute(buffer_c8_t buffer) {
|
||||
void AFSKRXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if( !configured ) {
|
||||
return;
|
||||
}
|
||||
/* Called every 2048/3072000 second -- 1500Hz. */
|
||||
|
||||
auto decimator_out = decimator.execute(buffer);
|
||||
|
||||
const buffer_c16_t work_baseband_buffer {
|
||||
(complex16_t*)decimator_out.p,
|
||||
sizeof(*decimator_out.p) * decimator_out.count
|
||||
};
|
||||
|
||||
/* 96kHz complex<int16_t>[64]
|
||||
* -> FIR filter, <6kHz (0.063fs) pass, gain 1.0
|
||||
* -> 48kHz int16_t[32] */
|
||||
auto channel = channel_filter.execute(decimator_out, work_baseband_buffer);
|
||||
|
||||
const buffer_s16_t work_audio_buffer {
|
||||
(int16_t*)decimator_out.p,
|
||||
sizeof(*decimator_out.p) * decimator_out.count
|
||||
};
|
||||
|
||||
/* 48kHz complex<int16_t>[32]
|
||||
* -> FM demodulation
|
||||
* -> 48kHz int16_t[32] */
|
||||
auto audio = demod.execute(channel, work_audio_buffer);
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
|
||||
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
|
||||
|
||||
auto audio = demod.execute(channel_out, work_audio_buffer);
|
||||
|
||||
/*static uint64_t audio_present_history = 0;
|
||||
const auto audio_present_now = squelch.execute(audio);
|
||||
@ -65,7 +52,7 @@ void AFSKRXProcessor::execute(buffer_c8_t buffer) {
|
||||
}*/
|
||||
//}
|
||||
|
||||
audio_hpf.execute_in_place(audio);
|
||||
//audio_hpf.execute_in_place(audio);
|
||||
|
||||
for(size_t i=0; i<audio.count; i++) {
|
||||
if (spur > 10) {
|
||||
@ -97,9 +84,9 @@ void AFSKRXProcessor::execute(buffer_c8_t buffer) {
|
||||
|
||||
if (sc >= 600) {
|
||||
sc = 0;
|
||||
AFSKDataMessage message;
|
||||
memcpy(message.data,aud,128*2);
|
||||
shared_memory.application_queue.push(message);
|
||||
//AFSKDataMessage message;
|
||||
//memcpy(message.data,aud,128*2);
|
||||
//shared_memory.application_queue.push(message);
|
||||
audc = 0;
|
||||
} else {
|
||||
sc++;
|
||||
@ -110,7 +97,7 @@ void AFSKRXProcessor::execute(buffer_c8_t buffer) {
|
||||
audc++;
|
||||
}
|
||||
|
||||
fill_audio_buffer(audio);
|
||||
audio_output.write(audio);
|
||||
}
|
||||
|
||||
void AFSKRXProcessor::data_handler(
|
||||
|
@ -24,38 +24,35 @@
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include "channel_decimator.hpp"
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_iir_config.hpp"
|
||||
#include "dsp_squelch.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <bitset>
|
||||
|
||||
class AFSKRXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
AFSKRXProcessor() {
|
||||
decimator.set_decimation_factor(ChannelDecimator::DecimationFactor::By32);
|
||||
channel_filter.configure(channel_filter_taps.taps, 2);
|
||||
}
|
||||
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
ChannelDecimator decimator;
|
||||
const fir_taps_real<64>& channel_filter_taps = taps_64_lp_042_078_tfilter;
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter;
|
||||
dsp::demodulate::FM demod { 48000, 5000 };
|
||||
|
||||
IIRBiquadFilter audio_hpf { audio_hpf_config };
|
||||
//FMSquelch squelch;
|
||||
std::array<complex16_t, 512> dst;
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
dst.size()
|
||||
};
|
||||
const buffer_f32_t work_audio_buffer {
|
||||
(float*)dst.data(),
|
||||
sizeof(dst) / sizeof(float)
|
||||
};
|
||||
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter;
|
||||
dsp::demodulate::FM demod; // 48000 5000
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0;
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1;
|
||||
|
||||
AudioOutput audio_output;
|
||||
|
||||
uint16_t bit_timer = 0, freq_timer = 0;
|
||||
uint16_t sc;
|
||||
uint8_t audc, spur, sign, prev_sign, bit = 0;
|
||||
@ -63,6 +60,9 @@ private:
|
||||
int16_t aud[128];
|
||||
|
||||
void data_handler(const double data);
|
||||
|
||||
bool configured { false };
|
||||
void configure(const NBFMConfigureMessage& message);
|
||||
};
|
||||
|
||||
#endif/*__PROC_TPMS_H__*/
|
||||
|
@ -24,46 +24,6 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
void SIGFRXProcessor::execute(buffer_c8_t buffer) {
|
||||
void SIGFRXProcessor::execute(const buffer_c8_t& buffer) {
|
||||
/* Called every 2048/3072000 second -- 1500Hz. */
|
||||
|
||||
auto decimator_out = decimator.execute(buffer);
|
||||
|
||||
const buffer_c16_t work_baseband_buffer {
|
||||
(complex16_t*)decimator_out.p,
|
||||
sizeof(*decimator_out.p) * decimator_out.count
|
||||
};
|
||||
|
||||
/* 192kHz complex<int16_t>[64]
|
||||
* -> 96kHz int16_t[32] */
|
||||
//auto channel = channel_filter.execute(decimator_out, work_baseband_buffer);
|
||||
|
||||
// TODO: Feed channel_stats post-decimation data?
|
||||
feed_channel_spectrum(
|
||||
decimator_out,
|
||||
41000, //decimator_out.sampling_rate * channel_filter_taps.pass_frequency_normalized,
|
||||
70000 //decimator_out.sampling_rate * channel_filter_taps.stop_frequency_normalized
|
||||
);
|
||||
|
||||
/*const buffer_s16_t work_audio_buffer {
|
||||
(int16_t*)decimator_out.p,
|
||||
sizeof(*decimator_out.p) * decimator_out.count
|
||||
};
|
||||
*
|
||||
auto audio = demod.execute(channel, work_audio_buffer);
|
||||
|
||||
static uint64_t audio_present_history = 0;
|
||||
const auto audio_present_now = squelch.execute(audio);
|
||||
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
|
||||
const bool audio_present = (audio_present_history != 0);
|
||||
|
||||
if( !audio_present ) {
|
||||
// Zero audio buffer.
|
||||
for(size_t i=0; i<audio.count; i++) {
|
||||
audio.p[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
audio_hpf.execute_in_place(audio);
|
||||
fill_audio_buffer(audio);*/
|
||||
}
|
||||
|
@ -24,31 +24,15 @@
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include "channel_decimator.hpp"
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_iir_config.hpp"
|
||||
#include "dsp_squelch.hpp"
|
||||
|
||||
class SIGFRXProcessor : public BasebandProcessor {
|
||||
public:
|
||||
SIGFRXProcessor() {
|
||||
decimator.set_decimation_factor(ChannelDecimator::DecimationFactor::By16);
|
||||
//channel_filter.configure(channel_filter_taps.taps, 1);
|
||||
}
|
||||
|
||||
void execute(buffer_c8_t buffer) override;
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
private:
|
||||
ChannelDecimator decimator;
|
||||
//const fir_taps_real<64>& channel_filter_taps = taps_64_lp_410_700_tfilter; //taps_64_lp_104_140_tfilter
|
||||
//dsp::decimate::FIRAndDecimateComplex channel_filter;
|
||||
dsp::demodulate::FM demod { 48000, 7500 };
|
||||
|
||||
IIRBiquadFilter audio_hpf { audio_hpf_config };
|
||||
FMSquelch squelch;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
BIN
firmware/bootstrap/bootstrap.bin
Executable file
BIN
firmware/bootstrap/bootstrap.bin
Executable file
Binary file not shown.
@ -2579,7 +2579,6 @@ FRESULT f_read (
|
||||
UINT rcnt, cc;
|
||||
BYTE csect, *rbuff = (BYTE*)buff;
|
||||
|
||||
|
||||
*br = 0; /* Clear read byte counter */
|
||||
|
||||
res = validate(fp); /* Check validity */
|
||||
|
@ -619,7 +619,7 @@ void sdc_lld_start_clk(SDCDriver *sdcp) {
|
||||
(void)sdcp;
|
||||
sdio_cclk_set_400khz();
|
||||
/* TODO: Reset card using CMD0 + init flag? */
|
||||
sdio_send_command(sdcp, 0 | (1U << 15), 0);
|
||||
if (sdio_send_command(sdcp, 0 | (1U << 15), 0) != CH_SUCCESS) for(;;);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -419,12 +419,14 @@ public:
|
||||
int64_t freq = 0;
|
||||
};
|
||||
|
||||
class DisplayFrameSyncMessage : public Message {
|
||||
class AFSKDataMessage : public Message {
|
||||
public:
|
||||
constexpr DisplayFrameSyncMessage(
|
||||
) : Message { ID::DisplayFrameSync }
|
||||
constexpr AFSKDataMessage(
|
||||
) : Message { ID::AFSKData }
|
||||
{
|
||||
}
|
||||
|
||||
int16_t data[128] = {0};
|
||||
};
|
||||
|
||||
class FIFOSignalMessage : public Message {
|
||||
|
@ -1,2 +1,2 @@
|
||||
const char md5_baseband[16] = {0x0f,0xf7,0xa8,0x6f,0x0f,0xb3,0x88,0x4c,0xec,0x45,0xcf,0x8d,0xd5,0xf8,0x11,0x92,};
|
||||
const char md5_baseband_tx[16] = {0x77,0xa8,0x27,0xec,0xb4,0xcb,0xe6,0x17,0x06,0x70,0x49,0x01,0xed,0x48,0x1f,0x54,};
|
||||
const char md5_baseband[16] = {0xf8,0xf3,0x7b,0x36,0x68,0xd3,0xa1,0x85,0x26,0xb1,0x76,0x99,0x46,0x95,0xfd,0xec,};
|
||||
const char md5_baseband_tx[16] = {0xfc,0xe9,0x57,0x6b,0xfb,0xac,0x72,0x61,0x65,0x4e,0x3d,0x7f,0xb4,0x78,0xbd,0xd2,};
|
||||
|
@ -1,24 +1,3 @@
|
||||
/*
|
||||
* 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 "ui_widget.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
|
||||
@ -305,77 +284,91 @@ void Text::set(const std::string value) {
|
||||
}
|
||||
|
||||
void Text::paint(Painter& painter) {
|
||||
if (style_ == nullptr) style_ = &style();
|
||||
const auto rect = screen_rect();
|
||||
const auto s = style();
|
||||
|
||||
painter.fill_rectangle(rect, s.background);
|
||||
|
||||
painter.draw_string(
|
||||
rect.pos,
|
||||
(*style_),
|
||||
s,
|
||||
text
|
||||
);
|
||||
}
|
||||
|
||||
/* Button ****************************************************************/
|
||||
/* Checkbox **************************************************************/
|
||||
|
||||
Button::Button(
|
||||
Rect parent_rect,
|
||||
std::string text
|
||||
) : Widget { parent_rect },
|
||||
text_ { text }
|
||||
{
|
||||
flags.focusable = true;
|
||||
}
|
||||
|
||||
void Button::set_text(const std::string value) {
|
||||
void Checkbox::set_text(const std::string value) {
|
||||
text_ = value;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
std::string Button::text() const {
|
||||
std::string Checkbox::text() const {
|
||||
return text_;
|
||||
}
|
||||
|
||||
void Button::paint(Painter& painter) {
|
||||
void Checkbox::set_value(const bool value) {
|
||||
value_ = value;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
bool Checkbox::value() const {
|
||||
return value_;
|
||||
}
|
||||
|
||||
void Checkbox::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
if (style_ == nullptr) style_ = &style();
|
||||
|
||||
const auto paint_style = (has_focus() || flags.highlighted) ? style_->invert() : *(style_);
|
||||
|
||||
painter.draw_rectangle(r, style().foreground);
|
||||
const auto paint_style = (has_focus() || flags.highlighted) ? style().invert() : style();
|
||||
|
||||
painter.draw_rectangle({ r.pos.x, r.pos.y, 24, 24 }, style().foreground);
|
||||
|
||||
painter.fill_rectangle(
|
||||
{ r.pos.x + 1, r.pos.y + 1, r.size.w - 2, r.size.h - 2 },
|
||||
paint_style.background
|
||||
{
|
||||
static_cast<Coord>(r.pos.x + 1), static_cast<Coord>(r.pos.y + 1),
|
||||
static_cast<Dim>(24 - 2), static_cast<Dim>(24 - 2)
|
||||
},
|
||||
style().background
|
||||
);
|
||||
|
||||
|
||||
painter.draw_rectangle({ r.pos.x+2, r.pos.y+2, 24-4, 24-4 }, paint_style.background);
|
||||
|
||||
if (value_ == true) {
|
||||
// Check
|
||||
portapack::display.draw_line( {r.pos.x+2, r.pos.y+14}, {r.pos.x+6, r.pos.y+18}, ui::Color::green());
|
||||
portapack::display.draw_line( {r.pos.x+6, r.pos.y+18}, {r.pos.x+20, r.pos.y+4}, ui::Color::green());
|
||||
} else {
|
||||
// Cross
|
||||
portapack::display.draw_line( {r.pos.x+1, r.pos.y+1}, {r.pos.x+24-2, r.pos.y+24-2}, ui::Color::red());
|
||||
portapack::display.draw_line( {r.pos.x+24-2, r.pos.y+1}, {r.pos.x+1, r.pos.y+24-2}, ui::Color::red());
|
||||
}
|
||||
|
||||
const auto label_r = paint_style.font.size_of(text_);
|
||||
painter.draw_string(
|
||||
{ r.pos.x + (r.size.w - label_r.w) / 2, r.pos.y + (r.size.h - label_r.h) / 2 },
|
||||
{
|
||||
static_cast<Coord>(r.pos.x + 24 + 4),
|
||||
static_cast<Coord>(r.pos.y + (24 - label_r.h) / 2)
|
||||
},
|
||||
paint_style,
|
||||
text_
|
||||
);
|
||||
}
|
||||
|
||||
bool Button::on_key(const KeyEvent key) {
|
||||
bool Checkbox::on_key(const KeyEvent key) {
|
||||
if( key == KeyEvent::Select ) {
|
||||
value_ = not value_;
|
||||
set_dirty();
|
||||
|
||||
if( on_select ) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if( on_dir ) {
|
||||
on_dir(*this, key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Button::on_touch(const TouchEvent event) {
|
||||
bool Checkbox::on_touch(const TouchEvent event) {
|
||||
switch(event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
flags.highlighted = true;
|
||||
@ -385,6 +378,7 @@ bool Button::on_touch(const TouchEvent event) {
|
||||
|
||||
case TouchEvent::Type::End:
|
||||
flags.highlighted = false;
|
||||
value_ = not value_;
|
||||
set_dirty();
|
||||
if( on_select ) {
|
||||
on_select(*this);
|
||||
@ -427,6 +421,199 @@ bool Button::on_touch(const TouchEvent event) {
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Button ****************************************************************/
|
||||
|
||||
Button::Button(
|
||||
Rect parent_rect,
|
||||
std::string text
|
||||
) : Widget { parent_rect },
|
||||
text_ { text }
|
||||
{
|
||||
flags.focusable = true;
|
||||
}
|
||||
|
||||
void Button::set_text(const std::string value) {
|
||||
text_ = value;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
std::string Button::text() const {
|
||||
return text_;
|
||||
}
|
||||
|
||||
void Button::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
const auto paint_style = (has_focus() || flags.highlighted) ? style().invert() : style();
|
||||
|
||||
painter.draw_rectangle(r, style().foreground);
|
||||
|
||||
painter.fill_rectangle(
|
||||
{ r.pos.x + 1, r.pos.y + 1, r.size.w - 2, r.size.h - 2 },
|
||||
paint_style.background
|
||||
);
|
||||
|
||||
const auto label_r = paint_style.font.size_of(text_);
|
||||
painter.draw_string(
|
||||
{ r.pos.x + (r.size.w - label_r.w) / 2, r.pos.y + (r.size.h - label_r.h) / 2 },
|
||||
paint_style,
|
||||
text_
|
||||
);
|
||||
}
|
||||
|
||||
bool Button::on_key(const KeyEvent key) {
|
||||
if( key == KeyEvent::Select ) {
|
||||
if( on_select ) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Button::on_touch(const TouchEvent event) {
|
||||
switch(event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
flags.highlighted = true;
|
||||
set_dirty();
|
||||
return true;
|
||||
|
||||
|
||||
case TouchEvent::Type::End:
|
||||
flags.highlighted = false;
|
||||
set_dirty();
|
||||
if( on_select ) {
|
||||
on_select(*this);
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#if 0
|
||||
switch(event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
flags.highlighted = true;
|
||||
set_dirty();
|
||||
return true;
|
||||
case TouchEvent::Type::Move:
|
||||
{
|
||||
const bool new_highlighted = screen_rect().contains(event.point);
|
||||
if( flags.highlighted != new_highlighted ) {
|
||||
flags.highlighted = new_highlighted;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
case TouchEvent::Type::End:
|
||||
if( flags.highlighted ) {
|
||||
flags.highlighted = false;
|
||||
set_dirty();
|
||||
if( on_select ) {
|
||||
on_select(*this);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Image *****************************************************************/
|
||||
|
||||
Image::Image(
|
||||
) : Image { { }, nullptr, Color::white(), Color::black() }
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image(
|
||||
const Rect parent_rect,
|
||||
const Bitmap* bitmap,
|
||||
const Color foreground,
|
||||
const Color background
|
||||
) : Widget { parent_rect },
|
||||
bitmap_ { bitmap },
|
||||
foreground_ { foreground },
|
||||
background_ { background }
|
||||
{
|
||||
}
|
||||
|
||||
void Image::set_bitmap(const Bitmap* bitmap) {
|
||||
bitmap_ = bitmap;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void Image::set_foreground(const Color color) {
|
||||
foreground_ = color;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void Image::set_background(const Color color) {
|
||||
background_ = color;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void Image::paint(Painter& painter) {
|
||||
if( bitmap_ ) {
|
||||
// Code also handles ImageButton behavior.
|
||||
const bool selected = (has_focus() || flags.highlighted);
|
||||
painter.draw_bitmap(
|
||||
screen_pos(),
|
||||
*bitmap_,
|
||||
selected ? background_ : foreground_,
|
||||
selected ? foreground_ : background_
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/* ImageButton ***********************************************************/
|
||||
|
||||
// TODO: Virtually all this code is duplicated from Button. Base class?
|
||||
|
||||
ImageButton::ImageButton(
|
||||
const Rect parent_rect,
|
||||
const Bitmap* bitmap,
|
||||
const Color foreground,
|
||||
const Color background
|
||||
) : Image { parent_rect, bitmap, foreground, background }
|
||||
{
|
||||
flags.focusable = true;
|
||||
}
|
||||
|
||||
bool ImageButton::on_key(const KeyEvent key) {
|
||||
if( key == KeyEvent::Select ) {
|
||||
if( on_select ) {
|
||||
on_select(*this);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ImageButton::on_touch(const TouchEvent event) {
|
||||
switch(event.type) {
|
||||
case TouchEvent::Type::Start:
|
||||
flags.highlighted = true;
|
||||
set_dirty();
|
||||
return true;
|
||||
|
||||
|
||||
case TouchEvent::Type::End:
|
||||
flags.highlighted = false;
|
||||
set_dirty();
|
||||
if( on_select ) {
|
||||
on_select(*this);
|
||||
}
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* OptionsField **********************************************************/
|
||||
|
||||
OptionsField::OptionsField(
|
||||
@ -480,6 +667,12 @@ void OptionsField::paint(Painter& painter) {
|
||||
}
|
||||
}
|
||||
|
||||
void OptionsField::on_focus() {
|
||||
if( on_show_options ) {
|
||||
on_show_options();
|
||||
}
|
||||
}
|
||||
|
||||
bool OptionsField::on_encoder(const EncoderEvent delta) {
|
||||
set_selected_index(selected_index() + delta);
|
||||
return true;
|
||||
|
@ -188,48 +188,11 @@ public:
|
||||
Text(Rect parent_rect);
|
||||
|
||||
void set(const std::string value);
|
||||
void set_style(const Style* new_style);
|
||||
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
std::string text;
|
||||
const Style* style_ { nullptr };
|
||||
};
|
||||
|
||||
class Checkbox : public Widget {
|
||||
public:
|
||||
std::function<void(Checkbox&)> on_select;
|
||||
|
||||
Checkbox(
|
||||
Point parent_point,
|
||||
std::string text
|
||||
) : Widget { parent_point },
|
||||
text_ { text }
|
||||
{
|
||||
flags.focusable = true;
|
||||
}
|
||||
|
||||
Checkbox(
|
||||
) : Checkbox { { }, { } }
|
||||
{
|
||||
}
|
||||
|
||||
void set_text(const std::string value);
|
||||
void set_style(const Style* new_style);
|
||||
std::string text() const;
|
||||
void set_value(const bool value);
|
||||
bool value() const;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
private:
|
||||
std::string text_;
|
||||
bool value_ = false;
|
||||
const Style* style_ { nullptr };
|
||||
};
|
||||
|
||||
class Button : public Widget {
|
||||
@ -244,8 +207,6 @@ public:
|
||||
}
|
||||
|
||||
void set_text(const std::string value);
|
||||
void set_text(const int value);
|
||||
void set_style(const Style* new_style);
|
||||
std::string text() const;
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
@ -255,7 +216,43 @@ public:
|
||||
|
||||
private:
|
||||
std::string text_;
|
||||
const Style* style_ { nullptr };
|
||||
};
|
||||
|
||||
class Image : public Widget {
|
||||
public:
|
||||
Image();
|
||||
Image(
|
||||
const Rect parent_rect,
|
||||
const Bitmap* bitmap,
|
||||
const Color foreground,
|
||||
const Color background
|
||||
);
|
||||
|
||||
void set_bitmap(const Bitmap* bitmap);
|
||||
void set_foreground(const Color color);
|
||||
void set_background(const Color color);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
const Bitmap* bitmap_;
|
||||
Color foreground_;
|
||||
Color background_;
|
||||
};
|
||||
|
||||
class ImageButton : public Image {
|
||||
public:
|
||||
std::function<void(ImageButton&)> on_select;
|
||||
|
||||
ImageButton(
|
||||
const Rect parent_rect,
|
||||
const Bitmap* bitmap,
|
||||
const Color foreground,
|
||||
const Color background
|
||||
);
|
||||
|
||||
bool on_key(const KeyEvent key) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
};
|
||||
|
||||
class OptionsField : public Widget {
|
||||
@ -266,6 +263,7 @@ public:
|
||||
using options_t = std::vector<option_t>;
|
||||
|
||||
std::function<void(size_t, value_t)> on_change;
|
||||
std::function<void(void)> on_show_options;
|
||||
|
||||
OptionsField(Point parent_pos, size_t length, options_t options);
|
||||
|
||||
@ -276,6 +274,7 @@ public:
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
void on_focus() override;
|
||||
bool on_encoder(const EncoderEvent delta) override;
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
|
Binary file not shown.
@ -55,8 +55,9 @@ sys.argv = sys.argv[1:]
|
||||
|
||||
# Format for module file:
|
||||
# Magic (4), Version (2), Length (4), Name (16), MD5 (16), Description (214)
|
||||
# Module binary...
|
||||
# MD5 (16)
|
||||
# 0x00 pad bytes (256)
|
||||
# Module binary (padded to 32768-16)
|
||||
# MD5 (16) again, so that module code can read it (dirty...)
|
||||
|
||||
for args in sys.argv:
|
||||
data = read_image(args + '/build/' + args + '.bin')
|
||||
@ -66,7 +67,7 @@ for args in sys.argv:
|
||||
info = 'PPM '
|
||||
|
||||
# Version
|
||||
info += struct.pack('H', 1)
|
||||
info += struct.pack('H', 2)
|
||||
|
||||
# Length
|
||||
info += struct.pack('I', len(data))
|
||||
@ -94,9 +95,12 @@ for args in sys.argv:
|
||||
description += (data_default_byte * pad_size)
|
||||
info += description
|
||||
|
||||
# Padding
|
||||
# Header padding to fit in SD card sector
|
||||
info += (data_default_byte * 256)
|
||||
|
||||
# Binary padding
|
||||
data = info + data
|
||||
pad_size = (32768 + 256 - 16) - len(data)
|
||||
pad_size = (32768 + 512 - 16) - len(data)
|
||||
data += (data_default_byte * pad_size)
|
||||
data += digest
|
||||
write_file(data, args + '.bin')
|
||||
@ -108,6 +112,6 @@ for args in sys.argv:
|
||||
h_data += 'const char md5_' + args.replace('-','_') + '[16] = {' + md5sum + '};\n'
|
||||
|
||||
# Update original binary with MD5 footprint
|
||||
write_file(data[256:(32768+256)], args + '/build/' + args + '.bin')
|
||||
write_file(data[512:(32768+512)], args + '/build/' + args + '.bin')
|
||||
|
||||
write_file(h_data, 'common/modules.h')
|
||||
|
Loading…
x
Reference in New Issue
Block a user