mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-12-28 16:59:47 -05:00
Merge remote-tracking branch 'upstream/master'
Conflicts: firmware/application/Makefile firmware/application/analog_audio_app.cpp firmware/application/analog_audio_app.hpp firmware/application/event.cpp firmware/application/irq_ipc.hpp firmware/application/portapack.hpp firmware/application/receiver_model.cpp firmware/application/receiver_model.hpp firmware/application/recent_entries.cpp firmware/application/string_format.hpp firmware/application/ui_debug.cpp firmware/application/ui_debug.hpp firmware/application/ui_menu.cpp firmware/application/ui_navigation.cpp firmware/application/ui_navigation.hpp firmware/application/ui_receiver.cpp firmware/application/ui_receiver.hpp firmware/application/ui_sd_card_status_view.cpp firmware/application/ui_sd_card_status_view.hpp firmware/application/ui_setup.cpp firmware/application/ui_setup.hpp firmware/application/ui_spectrum.hpp firmware/baseband-tx/dsp_fir_taps.cpp firmware/baseband-tx/dsp_fir_taps.hpp firmware/baseband-tx/irq_ipc_m4.cpp firmware/baseband-tx/irq_ipc_m4.hpp firmware/baseband-tx/proc_audiotx.cpp firmware/baseband/Makefile firmware/baseband/audio_output.cpp firmware/baseband/audio_output.hpp firmware/baseband/block_decimator.hpp firmware/baseband/dsp_decimate.cpp firmware/baseband/dsp_decimate.hpp firmware/baseband/dsp_demodulate.cpp firmware/baseband/dsp_demodulate.hpp firmware/baseband/dsp_fir_taps.cpp firmware/baseband/irq_ipc_m4.cpp firmware/baseband/irq_ipc_m4.hpp firmware/baseband/proc_am_audio.cpp firmware/baseband/proc_am_audio.hpp firmware/baseband/proc_nfm_audio.cpp firmware/baseband/proc_nfm_audio.hpp firmware/baseband/proc_wfm_audio.cpp firmware/baseband/proc_wfm_audio.hpp firmware/baseband/spectrum_collector.hpp firmware/common/dsp_fir_taps.cpp firmware/common/dsp_fir_taps.hpp firmware/common/event.hpp firmware/common/message.hpp firmware/common/ui_painter.cpp firmware/common/ui_painter.hpp
This commit is contained in:
commit
8009a9b543
@ -27,58 +27,273 @@ using namespace portapack;
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
AnalogAudioModel::AnalogAudioModel(ReceiverModel::Mode mode) {
|
||||
receiver_model.set_baseband_configuration({
|
||||
.mode = toUType(mode),
|
||||
.sampling_rate = 3072000,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
namespace ui {
|
||||
|
||||
switch(mode) {
|
||||
case ReceiverModel::Mode::NarrowbandFMAudio:
|
||||
configure_nbfm();
|
||||
break;
|
||||
/* AMOptionsView *********************************************************/
|
||||
|
||||
case ReceiverModel::Mode::WidebandFMAudio:
|
||||
configure_wfm();
|
||||
break;
|
||||
AMOptionsView::AMOptionsView(
|
||||
const Rect parent_rect, const Style* const style
|
||||
) : View { parent_rect }
|
||||
{
|
||||
set_style(style);
|
||||
|
||||
case ReceiverModel::Mode::AMAudio:
|
||||
configure_am();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
add_children({ {
|
||||
&label_config,
|
||||
&options_config,
|
||||
} });
|
||||
|
||||
options_config.set_selected_index(receiver_model.am_configuration());
|
||||
options_config.on_change = [this](size_t n, OptionsField::value_t) {
|
||||
receiver_model.set_am_configuration(n);
|
||||
};
|
||||
}
|
||||
|
||||
/* NBFMOptionsView *******************************************************/
|
||||
|
||||
NBFMOptionsView::NBFMOptionsView(
|
||||
const Rect parent_rect, const Style* const style
|
||||
) : View { parent_rect }
|
||||
{
|
||||
set_style(style);
|
||||
|
||||
add_children({ {
|
||||
&label_config,
|
||||
&options_config,
|
||||
} });
|
||||
|
||||
options_config.set_selected_index(receiver_model.nbfm_configuration());
|
||||
options_config.on_change = [this](size_t n, OptionsField::value_t) {
|
||||
receiver_model.set_nbfm_configuration(n);
|
||||
};
|
||||
}
|
||||
|
||||
/* AnalogAudioView *******************************************************/
|
||||
|
||||
AnalogAudioView::AnalogAudioView(
|
||||
NavigationView& nav
|
||||
) {
|
||||
add_children({ {
|
||||
&rssi,
|
||||
&channel,
|
||||
&audio,
|
||||
&field_frequency,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&options_modulation,
|
||||
&field_volume,
|
||||
&waterfall,
|
||||
} });
|
||||
|
||||
field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
// TODO: Provide separate modal method/scheme?
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
this->field_frequency.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
field_frequency.on_show_options = [this]() {
|
||||
this->on_show_options_frequency();
|
||||
};
|
||||
|
||||
field_lna.set_value(receiver_model.lna());
|
||||
field_lna.on_change = [this](int32_t v) {
|
||||
this->on_lna_changed(v);
|
||||
};
|
||||
|
||||
field_lna.on_show_options = [this]() {
|
||||
this->on_show_options_rf_gain();
|
||||
};
|
||||
|
||||
field_vga.set_value(receiver_model.vga());
|
||||
field_vga.on_change = [this](int32_t v_db) {
|
||||
this->on_vga_changed(v_db);
|
||||
};
|
||||
|
||||
const auto modulation = receiver_model.modulation();
|
||||
options_modulation.set_by_value(modulation);
|
||||
options_modulation.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
this->on_modulation_changed(static_cast<ReceiverModel::Mode>(v));
|
||||
};
|
||||
options_modulation.on_show_options = [this]() {
|
||||
this->on_show_options_modulation();
|
||||
};
|
||||
|
||||
field_volume.set_value((receiver_model.headphone_volume() - wolfson::wm8731::headphone_gain_range.max).decibel() + 99);
|
||||
field_volume.on_change = [this](int32_t v) {
|
||||
this->on_headphone_volume_changed(v);
|
||||
};
|
||||
|
||||
update_modulation(static_cast<ReceiverModel::Mode>(modulation));
|
||||
}
|
||||
|
||||
AnalogAudioView::~AnalogAudioView() {
|
||||
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
|
||||
// both?
|
||||
audio_codec.headphone_mute();
|
||||
|
||||
receiver_model.disable();
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_hide() {
|
||||
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
|
||||
// it's being shown or hidden.
|
||||
waterfall.on_hide();
|
||||
View::on_hide();
|
||||
}
|
||||
|
||||
void AnalogAudioView::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), static_cast<ui::Dim>(new_parent_rect.height() - header_height) };
|
||||
waterfall.set_parent_rect(waterfall_rect);
|
||||
}
|
||||
|
||||
void AnalogAudioView::focus() {
|
||||
field_frequency.focus();
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) {
|
||||
receiver_model.set_baseband_bandwidth(bandwidth_hz);
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_rf_amp_changed(bool v) {
|
||||
receiver_model.set_rf_amp(v);
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_lna_changed(int32_t v_db) {
|
||||
receiver_model.set_lna(v_db);
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_vga_changed(int32_t v_db) {
|
||||
receiver_model.set_vga(v_db);
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_modulation_changed(const ReceiverModel::Mode modulation) {
|
||||
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
|
||||
// it's being shown or hidden.
|
||||
waterfall.on_hide();
|
||||
update_modulation(modulation);
|
||||
on_show_options_modulation();
|
||||
waterfall.on_show();
|
||||
}
|
||||
|
||||
void AnalogAudioView::remove_options_widget() {
|
||||
if( options_widget ) {
|
||||
remove_child(options_widget.get());
|
||||
options_widget.reset();
|
||||
}
|
||||
|
||||
field_lna.set_style(nullptr);
|
||||
options_modulation.set_style(nullptr);
|
||||
field_frequency.set_style(nullptr);
|
||||
}
|
||||
|
||||
void AnalogAudioModel::configure_nbfm() {
|
||||
const NBFMConfigureMessage message {
|
||||
taps_4k25_decim_0,
|
||||
taps_4k25_decim_1,
|
||||
taps_4k25_channel,
|
||||
2500,
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
void AnalogAudioView::set_options_widget(std::unique_ptr<Widget> new_widget) {
|
||||
if( new_widget ) {
|
||||
options_widget = std::move(new_widget);
|
||||
add_child(options_widget.get());
|
||||
}
|
||||
}
|
||||
|
||||
void AnalogAudioModel::configure_wfm() {
|
||||
const WFMConfigureMessage message {
|
||||
taps_200k_wfm_decim_0,
|
||||
taps_200k_wfm_decim_1,
|
||||
taps_64_lp_156_198,
|
||||
75000,
|
||||
void AnalogAudioView::on_show_options_frequency() {
|
||||
// TODO: This approach of managing options views is error-prone and unsustainable!
|
||||
remove_options_widget();
|
||||
|
||||
field_frequency.set_style(&style_options_group);
|
||||
|
||||
auto widget = std::make_unique<FrequencyOptionsView>(
|
||||
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
|
||||
&style_options_group
|
||||
);
|
||||
|
||||
widget->set_step(receiver_model.frequency_step());
|
||||
widget->on_change_step = [this](rf::Frequency f) {
|
||||
this->on_frequency_step_changed(f);
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
widget->set_reference_ppm_correction(receiver_model.reference_ppm_correction());
|
||||
widget->on_change_reference_ppm_correction = [this](int32_t v) {
|
||||
this->on_reference_ppm_correction_changed(v);
|
||||
};
|
||||
|
||||
set_options_widget(std::move(widget));
|
||||
}
|
||||
|
||||
void AnalogAudioModel::configure_am() {
|
||||
const AMConfigureMessage message {
|
||||
taps_6k0_decim_0,
|
||||
taps_6k0_decim_1,
|
||||
taps_6k0_channel,
|
||||
void AnalogAudioView::on_show_options_rf_gain() {
|
||||
// TODO: This approach of managing options views is error-prone and unsustainable!
|
||||
remove_options_widget();
|
||||
|
||||
field_lna.set_style(&style_options_group);
|
||||
|
||||
auto widget = std::make_unique<RadioGainOptionsView>(
|
||||
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
|
||||
&style_options_group
|
||||
);
|
||||
|
||||
widget->set_rf_amp(receiver_model.rf_amp());
|
||||
widget->on_change_rf_amp = [this](bool enable) {
|
||||
this->on_rf_amp_changed(enable);
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
|
||||
set_options_widget(std::move(widget));
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_show_options_modulation() {
|
||||
// TODO: This approach of managing options views is error-prone and unsustainable!
|
||||
remove_options_widget();
|
||||
|
||||
const auto modulation = static_cast<ReceiverModel::Mode>(receiver_model.modulation());
|
||||
if( modulation == ReceiverModel::Mode::AMAudio ) {
|
||||
options_modulation.set_style(&style_options_group);
|
||||
auto widget = std::make_unique<AMOptionsView>(
|
||||
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
|
||||
&style_options_group
|
||||
);
|
||||
set_options_widget(std::move(widget));
|
||||
}
|
||||
if( modulation == ReceiverModel::Mode::NarrowbandFMAudio ) {
|
||||
options_modulation.set_style(&style_options_group);
|
||||
auto widget = std::make_unique<NBFMOptionsView>(
|
||||
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
|
||||
&style_options_group
|
||||
);
|
||||
set_options_widget(std::move(widget));
|
||||
}
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_frequency_step_changed(rf::Frequency f) {
|
||||
receiver_model.set_frequency_step(f);
|
||||
field_frequency.set_step(f);
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_reference_ppm_correction_changed(int32_t v) {
|
||||
receiver_model.set_reference_ppm_correction(v);
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_headphone_volume_changed(int32_t v) {
|
||||
const auto new_volume = volume_t::decibel(v - 99) + wolfson::wm8731::headphone_gain_range.max;
|
||||
receiver_model.set_headphone_volume(new_volume);
|
||||
}
|
||||
|
||||
void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
|
||||
const auto is_wideband_spectrum_mode = (modulation == ReceiverModel::Mode::SpectrumAnalysis);
|
||||
receiver_model.set_baseband_configuration({
|
||||
.mode = toUType(modulation),
|
||||
.sampling_rate = is_wideband_spectrum_mode ? 20000000U : 3072000U,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
receiver_model.set_baseband_bandwidth(is_wideband_spectrum_mode ? 12000000 : 1750000);
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
@ -23,30 +23,145 @@
|
||||
#define __ANALOG_AUDIO_APP_H__
|
||||
|
||||
#include "receiver_model.hpp"
|
||||
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
|
||||
class AnalogAudioModel {
|
||||
public:
|
||||
AnalogAudioModel(ReceiverModel::Mode mode);
|
||||
|
||||
private:
|
||||
void configure_nbfm();
|
||||
void configure_wfm();
|
||||
void configure_am();
|
||||
};
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class AnalogAudioView : public spectrum::WaterfallWidget {
|
||||
constexpr Style style_options_group {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::blue(),
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
|
||||
class AMOptionsView : public View {
|
||||
public:
|
||||
AnalogAudioView(
|
||||
ReceiverModel::Mode mode
|
||||
) : model { mode }
|
||||
{
|
||||
}
|
||||
AMOptionsView(const Rect parent_rect, const Style* const style);
|
||||
|
||||
private:
|
||||
AnalogAudioModel model;
|
||||
Text label_config {
|
||||
{ 0 * 8, 0 * 16, 2 * 8, 1 * 16 },
|
||||
"BW",
|
||||
};
|
||||
|
||||
OptionsField options_config {
|
||||
{ 3 * 8, 0 * 16 },
|
||||
4,
|
||||
{
|
||||
{ "DSB ", 0 },
|
||||
{ "USB ", 0 },
|
||||
{ "LSB ", 0 },
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class NBFMOptionsView : public View {
|
||||
public:
|
||||
NBFMOptionsView(const Rect parent_rect, const Style* const style);
|
||||
|
||||
private:
|
||||
Text label_config {
|
||||
{ 0 * 8, 0 * 16, 2 * 8, 1 * 16 },
|
||||
"BW",
|
||||
};
|
||||
|
||||
OptionsField options_config {
|
||||
{ 3 * 8, 0 * 16 },
|
||||
4,
|
||||
{
|
||||
{ " 8k5", 0 },
|
||||
{ "11k ", 0 },
|
||||
{ "16k ", 0 },
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class AnalogAudioView : public View {
|
||||
public:
|
||||
AnalogAudioView(NavigationView& nav);
|
||||
~AnalogAudioView();
|
||||
|
||||
void on_hide() override;
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
static constexpr ui::Dim header_height = 2 * 16;
|
||||
|
||||
RSSI rssi {
|
||||
{ 21 * 8, 0, 6 * 8, 4 },
|
||||
};
|
||||
|
||||
Channel channel {
|
||||
{ 21 * 8, 5, 6 * 8, 4 },
|
||||
};
|
||||
|
||||
Audio audio {
|
||||
{ 21 * 8, 10, 6 * 8, 4 },
|
||||
};
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 5 * 8, 0 * 16 },
|
||||
};
|
||||
|
||||
LNAGainField field_lna {
|
||||
{ 15 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
NumberField field_vga {
|
||||
{ 18 * 8, 0 * 16},
|
||||
2,
|
||||
{ max2837::vga::gain_db_range.minimum, max2837::vga::gain_db_range.maximum },
|
||||
max2837::vga::gain_db_step,
|
||||
' ',
|
||||
};
|
||||
|
||||
OptionsField options_modulation {
|
||||
{ 0 * 8, 0 * 16 },
|
||||
4,
|
||||
{
|
||||
{ " AM ", toUType(ReceiverModel::Mode::AMAudio) },
|
||||
{ "NFM ", toUType(ReceiverModel::Mode::NarrowbandFMAudio) },
|
||||
{ "WFM ", toUType(ReceiverModel::Mode::WidebandFMAudio) },
|
||||
{ "SPEC", toUType(ReceiverModel::Mode::SpectrumAnalysis) },
|
||||
}
|
||||
};
|
||||
|
||||
NumberField field_volume {
|
||||
{ 28 * 8, 0 * 16 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
std::unique_ptr<Widget> options_widget;
|
||||
|
||||
spectrum::WaterfallWidget waterfall;
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
|
||||
void on_rf_amp_changed(bool v);
|
||||
void on_lna_changed(int32_t v_db);
|
||||
void on_vga_changed(int32_t v_db);
|
||||
void on_modulation_changed(const ReceiverModel::Mode modulation);
|
||||
void on_show_options_frequency();
|
||||
void on_show_options_rf_gain();
|
||||
void on_show_options_modulation();
|
||||
void on_frequency_step_changed(rf::Frequency f);
|
||||
void on_reference_ppm_correction_changed(int32_t v);
|
||||
void on_headphone_volume_changed(int32_t v);
|
||||
void on_edit_frequency();
|
||||
|
||||
void remove_options_widget();
|
||||
void set_options_widget(std::unique_ptr<Widget> new_widget);
|
||||
|
||||
void update_modulation(const ReceiverModel::Mode modulation);
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
@ -516,7 +516,17 @@ void ClockManager::start_audio_pll() {
|
||||
while( !cgu::pll0audio::is_locked() );
|
||||
cgu::pll0audio::clock_enable();
|
||||
|
||||
set_clock(LPC_CGU->BASE_AUDIO_CLK, cgu::CLK_SEL::PLL0AUDIO);
|
||||
set_base_audio_clock_divider(1);
|
||||
set_clock(LPC_CGU->BASE_AUDIO_CLK, cgu::CLK_SEL::IDIVC);
|
||||
}
|
||||
|
||||
void ClockManager::set_base_audio_clock_divider(const size_t divisor) {
|
||||
LPC_CGU->IDIVC_CTRL =
|
||||
(0 << 1)
|
||||
| ((divisor - 1) << 2)
|
||||
| (1 << 11)
|
||||
| (toUType(cgu::CLK_SEL::PLL0AUDIO) << 24)
|
||||
;
|
||||
}
|
||||
|
||||
void ClockManager::stop_audio_pll() {
|
||||
|
@ -51,6 +51,8 @@ public:
|
||||
void start_audio_pll();
|
||||
void stop_audio_pll();
|
||||
|
||||
void set_base_audio_clock_divider(const size_t divisor);
|
||||
|
||||
void enable_codec_clocks();
|
||||
void disable_codec_clocks();
|
||||
|
||||
|
@ -61,9 +61,9 @@ ClockManager clock_manager {
|
||||
i2c0, clock_generator
|
||||
};
|
||||
|
||||
ReceiverModel receiver_model {
|
||||
clock_manager
|
||||
};
|
||||
ReceiverModel receiver_model;
|
||||
|
||||
TemperatureLogger temperature_logger;
|
||||
|
||||
TemperatureLogger temperature_logger;
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "lcd_ili9341.hpp"
|
||||
|
||||
#include "radio.hpp"
|
||||
#include "clock_manager.hpp"
|
||||
#include "temperature_logger.hpp"
|
||||
|
||||
namespace portapack {
|
||||
@ -44,6 +45,7 @@ extern SPI ssp1;
|
||||
extern wolfson::wm8731::WM8731 audio_codec;
|
||||
|
||||
extern si5351::Si5351 clock_generator;
|
||||
extern ClockManager clock_manager;
|
||||
|
||||
extern ReceiverModel receiver_model;
|
||||
extern TransmitterModel transmitter_model;
|
||||
|
@ -26,6 +26,90 @@
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_iir_config.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
struct AMConfig {
|
||||
const fir_taps_complex<64> channel;
|
||||
const AMConfigureMessage::Modulation modulation;
|
||||
|
||||
void apply() const;
|
||||
};
|
||||
|
||||
struct NBFMConfig {
|
||||
const fir_taps_real<24> decim_0;
|
||||
const fir_taps_real<32> decim_1;
|
||||
const fir_taps_real<32> channel;
|
||||
const size_t deviation;
|
||||
|
||||
void apply() const;
|
||||
};
|
||||
|
||||
struct WFMConfig {
|
||||
void apply() const;
|
||||
};
|
||||
|
||||
void AMConfig::apply() const {
|
||||
const AMConfigureMessage message {
|
||||
taps_6k0_decim_0,
|
||||
taps_6k0_decim_1,
|
||||
taps_6k0_decim_2,
|
||||
channel,
|
||||
modulation,
|
||||
audio_12k_hpf_300hz_config
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
clock_manager.set_base_audio_clock_divider(4);
|
||||
}
|
||||
|
||||
void NBFMConfig::apply() const {
|
||||
const NBFMConfigureMessage message {
|
||||
decim_0,
|
||||
decim_1,
|
||||
channel,
|
||||
2,
|
||||
deviation,
|
||||
audio_24k_hpf_300hz_config,
|
||||
audio_24k_deemph_300_6_config
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
clock_manager.set_base_audio_clock_divider(2);
|
||||
}
|
||||
|
||||
void WFMConfig::apply() const {
|
||||
const WFMConfigureMessage message {
|
||||
taps_200k_wfm_decim_0,
|
||||
taps_200k_wfm_decim_1,
|
||||
taps_64_lp_156_198,
|
||||
75000,
|
||||
audio_48k_hpf_30hz_config,
|
||||
audio_48k_deemph_2122_6_config
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
clock_manager.set_base_audio_clock_divider(1);
|
||||
}
|
||||
|
||||
static constexpr std::array<AMConfig, 3> am_configs { {
|
||||
{ taps_6k0_dsb_channel, AMConfigureMessage::Modulation::DSB },
|
||||
{ taps_2k8_usb_channel, AMConfigureMessage::Modulation::SSB },
|
||||
{ taps_2k8_lsb_channel, AMConfigureMessage::Modulation::SSB },
|
||||
} };
|
||||
|
||||
static constexpr std::array<NBFMConfig, 3> nbfm_configs { {
|
||||
{ taps_4k25_decim_0, taps_4k25_decim_1, taps_4k25_channel, 2500 },
|
||||
{ taps_11k0_decim_0, taps_11k0_decim_1, taps_11k0_channel, 2500 },
|
||||
{ taps_16k0_decim_0, taps_16k0_decim_1, taps_16k0_channel, 5000 },
|
||||
} };
|
||||
|
||||
static constexpr std::array<WFMConfig, 1> wfm_configs { {
|
||||
{ },
|
||||
} };
|
||||
|
||||
} /* namespace */
|
||||
|
||||
rf::Frequency ReceiverModel::tuning_frequency() const {
|
||||
return persistent_memory::tuned_frequency();
|
||||
}
|
||||
@ -129,7 +213,7 @@ void ReceiverModel::enable() {
|
||||
update_vga();
|
||||
update_baseband_bandwidth();
|
||||
update_baseband_configuration();
|
||||
|
||||
update_modulation_configuration();
|
||||
update_headphone_volume();
|
||||
}
|
||||
|
||||
@ -189,6 +273,27 @@ void ReceiverModel::set_baseband_configuration(const BasebandConfiguration confi
|
||||
update_baseband_configuration();
|
||||
}
|
||||
|
||||
void ReceiverModel::set_am_configuration(const size_t n) {
|
||||
if( n < am_configs.size() ) {
|
||||
am_config_index = n;
|
||||
update_modulation_configuration();
|
||||
}
|
||||
}
|
||||
|
||||
void ReceiverModel::set_nbfm_configuration(const size_t n) {
|
||||
if( n < nbfm_configs.size() ) {
|
||||
nbfm_config_index = n;
|
||||
update_modulation_configuration();
|
||||
}
|
||||
}
|
||||
|
||||
void ReceiverModel::set_wfm_configuration(const size_t n) {
|
||||
if( n < wfm_configs.size() ) {
|
||||
wfm_config_index = n;
|
||||
update_modulation_configuration();
|
||||
}
|
||||
}
|
||||
|
||||
void ReceiverModel::update_baseband_configuration() {
|
||||
// TODO: Move more low-level radio control stuff to M4. It'll enable tighter
|
||||
// synchronization for things like wideband (sweeping) spectrum analysis, and
|
||||
@ -211,3 +316,47 @@ void ReceiverModel::update_headphone_volume() {
|
||||
|
||||
audio_codec.set_headphone_volume(headphone_volume_);
|
||||
}
|
||||
|
||||
void ReceiverModel::update_modulation_configuration() {
|
||||
switch(static_cast<Mode>(modulation())) {
|
||||
default:
|
||||
case Mode::AMAudio:
|
||||
update_am_configuration();
|
||||
break;
|
||||
|
||||
case Mode::NarrowbandFMAudio:
|
||||
update_nbfm_configuration();
|
||||
break;
|
||||
|
||||
case Mode::WidebandFMAudio:
|
||||
update_wfm_configuration();
|
||||
break;
|
||||
|
||||
case Mode::SpectrumAnalysis:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
size_t ReceiverModel::am_configuration() const {
|
||||
return am_config_index;
|
||||
}
|
||||
|
||||
void ReceiverModel::update_am_configuration() {
|
||||
am_configs[am_config_index].apply();
|
||||
}
|
||||
|
||||
size_t ReceiverModel::nbfm_configuration() const {
|
||||
return nbfm_config_index;
|
||||
}
|
||||
|
||||
void ReceiverModel::update_nbfm_configuration() {
|
||||
nbfm_configs[nbfm_config_index].apply();
|
||||
}
|
||||
|
||||
size_t ReceiverModel::wfm_configuration() const {
|
||||
return wfm_config_index;
|
||||
}
|
||||
|
||||
void ReceiverModel::update_wfm_configuration() {
|
||||
wfm_configs[wfm_config_index].apply();
|
||||
}
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include "clock_manager.hpp"
|
||||
#include "message.hpp"
|
||||
#include "rf_path.hpp"
|
||||
#include "max2837.hpp"
|
||||
@ -43,12 +42,6 @@ public:
|
||||
ERT = 6,
|
||||
};
|
||||
|
||||
constexpr ReceiverModel(
|
||||
ClockManager& clock_manager
|
||||
) : clock_manager(clock_manager)
|
||||
{
|
||||
}
|
||||
|
||||
rf::Frequency tuning_frequency() const;
|
||||
void set_tuning_frequency(rf::Frequency f);
|
||||
|
||||
@ -87,6 +80,15 @@ public:
|
||||
|
||||
void set_baseband_configuration(const BasebandConfiguration config);
|
||||
|
||||
size_t am_configuration() const;
|
||||
void set_am_configuration(const size_t n);
|
||||
|
||||
size_t nbfm_configuration() const;
|
||||
void set_nbfm_configuration(const size_t n);
|
||||
|
||||
size_t wfm_configuration() const;
|
||||
void set_wfm_configuration(const size_t n);
|
||||
|
||||
private:
|
||||
rf::Frequency frequency_step_ { 25000 };
|
||||
bool enabled_ { false };
|
||||
@ -100,8 +102,10 @@ private:
|
||||
.sampling_rate = 3072000,
|
||||
.decimation_factor = 1,
|
||||
};
|
||||
size_t am_config_index = 0;
|
||||
size_t nbfm_config_index = 0;
|
||||
size_t wfm_config_index = 0;
|
||||
volume_t headphone_volume_ { -43.0_dB };
|
||||
ClockManager& clock_manager;
|
||||
|
||||
int32_t tuning_offset();
|
||||
|
||||
@ -114,6 +118,11 @@ private:
|
||||
void update_baseband_configuration();
|
||||
void update_headphone_volume();
|
||||
|
||||
void update_modulation_configuration();
|
||||
void update_am_configuration();
|
||||
void update_nbfm_configuration();
|
||||
void update_wfm_configuration();
|
||||
|
||||
void baseband_disable();
|
||||
};
|
||||
|
||||
|
@ -23,11 +23,6 @@
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
#include "ff.h"
|
||||
#include "hackrf_gpio.hpp"
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "radio.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "ui_sigfrx.hpp"
|
||||
#include "ui_numbers.hpp"
|
||||
|
||||
#include "analog_audio_app.hpp"
|
||||
#include "ais_app.hpp"
|
||||
#include "ert_app.hpp"
|
||||
#include "tpms_app.hpp"
|
||||
@ -65,7 +66,6 @@ SystemStatusView::SystemStatusView() {
|
||||
&button_sleep,
|
||||
&sd_card_status_view,
|
||||
} });
|
||||
sd_card_status_view.set_parent_rect({ 28 * 8, 0 * 16, 2 * 8, 1 * 16 });
|
||||
|
||||
button_back.on_select = [this](Button&){
|
||||
if( this->on_back ) {
|
||||
@ -73,7 +73,7 @@ SystemStatusView::SystemStatusView() {
|
||||
}
|
||||
};
|
||||
|
||||
button_sleep.on_select = [this](Button&) {
|
||||
button_sleep.on_select = [this](ImageButton&) {
|
||||
DisplaySleepMessage message;
|
||||
EventDispatcher::message_map().send(&message);
|
||||
};
|
||||
|
@ -37,6 +37,29 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
static constexpr uint8_t bitmap_sleep_data[] = {
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x04,
|
||||
0x00, 0x08,
|
||||
0x00, 0x18,
|
||||
0x00, 0x18,
|
||||
0x00, 0x38,
|
||||
0x00, 0x3c,
|
||||
0x00, 0x3c,
|
||||
0x00, 0x3e,
|
||||
0x84, 0x1f,
|
||||
0xf8, 0x1f,
|
||||
0xf0, 0x0f,
|
||||
0xc0, 0x03,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
};
|
||||
|
||||
static constexpr Bitmap bitmap_sleep {
|
||||
{ 16, 16 }, bitmap_sleep_data
|
||||
};
|
||||
|
||||
class SystemStatusView : public View {
|
||||
public:
|
||||
std::function<void(void)> on_back;
|
||||
@ -47,7 +70,7 @@ public:
|
||||
void set_title(const std::string new_value);
|
||||
|
||||
private:
|
||||
static constexpr auto default_title = "PortaPack/HAVOC";
|
||||
static constexpr auto default_title = "PortaPack";
|
||||
|
||||
Button button_back {
|
||||
{ 0 * 8, 0 * 16, 3 * 8, 16 },
|
||||
@ -59,12 +82,16 @@ private:
|
||||
default_title,
|
||||
};
|
||||
|
||||
Button button_sleep {
|
||||
ImageButton button_sleep {
|
||||
{ 25 * 8, 0, 2 * 8, 1 * 16 },
|
||||
"ZZ",
|
||||
&bitmap_sleep,
|
||||
Color::white(),
|
||||
Color::black()
|
||||
};
|
||||
|
||||
SDCardStatusView sd_card_status_view;
|
||||
SDCardStatusView sd_card_status_view {
|
||||
{ 28 * 8, 0 * 16, 2 * 8, 1 * 16 }
|
||||
};
|
||||
};
|
||||
|
||||
class NavigationView : public View {
|
||||
|
@ -26,11 +26,7 @@ using namespace portapack;
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "analog_audio_app.hpp"
|
||||
#include "ais_app.hpp"
|
||||
#include "tpms_app.hpp"
|
||||
#include "ert_app.hpp"
|
||||
#include "spectrum_analysis_app.hpp"
|
||||
#include "max2837.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
@ -322,184 +318,4 @@ void LNAGainField::on_focus() {
|
||||
}
|
||||
}
|
||||
|
||||
/* ReceiverView **********************************************************/
|
||||
|
||||
ReceiverView::ReceiverView(
|
||||
NavigationView& nav
|
||||
) {
|
||||
add_children({ {
|
||||
&rssi,
|
||||
&channel,
|
||||
&audio,
|
||||
&field_frequency,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&options_modulation,
|
||||
&field_volume,
|
||||
&view_frequency_options,
|
||||
&view_rf_gain_options,
|
||||
} });
|
||||
|
||||
field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
// TODO: Provide separate modal method/scheme?
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
this->field_frequency.set_value(f);
|
||||
};
|
||||
};
|
||||
field_frequency.on_show_options = [this]() {
|
||||
this->on_show_options_frequency();
|
||||
};
|
||||
|
||||
field_lna.set_value(receiver_model.lna());
|
||||
field_lna.on_change = [this](int32_t v) {
|
||||
this->on_lna_changed(v);
|
||||
};
|
||||
field_lna.on_show_options = [this]() {
|
||||
this->on_show_options_rf_gain();
|
||||
};
|
||||
|
||||
field_vga.set_value(receiver_model.vga());
|
||||
field_vga.on_change = [this](int32_t v_db) {
|
||||
this->on_vga_changed(v_db);
|
||||
};
|
||||
|
||||
options_modulation.set_by_value(receiver_model.modulation());
|
||||
options_modulation.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
(void)n;
|
||||
this->on_modulation_changed(static_cast<ReceiverModel::Mode>(v));
|
||||
};
|
||||
|
||||
field_volume.set_value((receiver_model.headphone_volume() - wolfson::wm8731::headphone_gain_range.max).decibel() + 99);
|
||||
field_volume.on_change = [this](int32_t v) {
|
||||
this->on_headphone_volume_changed(v);
|
||||
};
|
||||
|
||||
view_frequency_options.hidden(true);
|
||||
view_frequency_options.set_step(receiver_model.frequency_step());
|
||||
view_frequency_options.on_change_step = [this](rf::Frequency f) {
|
||||
this->on_frequency_step_changed(f);
|
||||
};
|
||||
view_frequency_options.set_reference_ppm_correction(receiver_model.reference_ppm_correction());
|
||||
view_frequency_options.on_change_reference_ppm_correction = [this](int32_t v) {
|
||||
this->on_reference_ppm_correction_changed(v);
|
||||
};
|
||||
|
||||
view_rf_gain_options.hidden(true);
|
||||
view_rf_gain_options.set_rf_amp(receiver_model.rf_amp());
|
||||
view_rf_gain_options.on_change_rf_amp = [this](bool enable) {
|
||||
this->on_rf_amp_changed(enable);
|
||||
};
|
||||
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
ReceiverView::~ReceiverView() {
|
||||
|
||||
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
|
||||
// both?
|
||||
audio_codec.headphone_mute();
|
||||
|
||||
receiver_model.disable();
|
||||
}
|
||||
|
||||
void ReceiverView::on_show() {
|
||||
View::on_show();
|
||||
|
||||
// TODO: Separate concepts of baseband "modulation" and receiver "mode".
|
||||
on_modulation_changed(static_cast<ReceiverModel::Mode>(receiver_model.modulation()));
|
||||
}
|
||||
|
||||
void ReceiverView::on_hide() {
|
||||
on_modulation_changed(static_cast<ReceiverModel::Mode>(-1));
|
||||
|
||||
View::on_hide();
|
||||
}
|
||||
|
||||
void ReceiverView::focus() {
|
||||
field_frequency.focus();
|
||||
}
|
||||
|
||||
void ReceiverView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
void ReceiverView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) {
|
||||
receiver_model.set_baseband_bandwidth(bandwidth_hz);
|
||||
}
|
||||
|
||||
void ReceiverView::on_rf_amp_changed(bool v) {
|
||||
receiver_model.set_rf_amp(v);
|
||||
}
|
||||
|
||||
void ReceiverView::on_lna_changed(int32_t v_db) {
|
||||
receiver_model.set_lna(v_db);
|
||||
}
|
||||
|
||||
void ReceiverView::on_vga_changed(int32_t v_db) {
|
||||
receiver_model.set_vga(v_db);
|
||||
}
|
||||
|
||||
void ReceiverView::on_modulation_changed(ReceiverModel::Mode mode) {
|
||||
remove_child(widget_content.get());
|
||||
widget_content.reset();
|
||||
|
||||
switch(mode) {
|
||||
case ReceiverModel::Mode::AMAudio:
|
||||
case ReceiverModel::Mode::NarrowbandFMAudio:
|
||||
case ReceiverModel::Mode::WidebandFMAudio:
|
||||
widget_content = std::make_unique<AnalogAudioView>(mode);
|
||||
break;
|
||||
|
||||
case ReceiverModel::Mode::SpectrumAnalysis:
|
||||
widget_content = std::make_unique<SpectrumAnalysisView>();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if( widget_content ) {
|
||||
add_child(widget_content.get());
|
||||
const ui::Rect rect { 0, header_height, parent_rect.width(), static_cast<ui::Dim>(parent_rect.height() - header_height) };
|
||||
widget_content->set_parent_rect(rect);
|
||||
}
|
||||
}
|
||||
|
||||
void ReceiverView::on_show_options_frequency() {
|
||||
view_rf_gain_options.hidden(true);
|
||||
field_lna.set_style(nullptr);
|
||||
|
||||
view_frequency_options.hidden(false);
|
||||
field_frequency.set_style(&view_frequency_options.style());
|
||||
}
|
||||
|
||||
void ReceiverView::on_show_options_rf_gain() {
|
||||
view_frequency_options.hidden(true);
|
||||
field_frequency.set_style(nullptr);
|
||||
|
||||
view_rf_gain_options.hidden(false);
|
||||
field_lna.set_style(&view_frequency_options.style());
|
||||
}
|
||||
|
||||
void ReceiverView::on_frequency_step_changed(rf::Frequency f) {
|
||||
receiver_model.set_frequency_step(f);
|
||||
field_frequency.set_step(f);
|
||||
}
|
||||
|
||||
void ReceiverView::on_reference_ppm_correction_changed(int32_t v) {
|
||||
receiver_model.set_reference_ppm_correction(v);
|
||||
}
|
||||
|
||||
void ReceiverView::on_headphone_volume_changed(int32_t v) {
|
||||
const auto new_volume = volume_t::decibel(v - 99) + wolfson::wm8731::headphone_gain_range.max;
|
||||
receiver_model.set_headphone_volume(new_volume);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
@ -23,19 +23,11 @@
|
||||
#define __UI_RECEIVER_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_painter.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "max2837.hpp"
|
||||
#include "rf_path.hpp"
|
||||
#include "volume.hpp"
|
||||
#include "wm8731.hpp"
|
||||
|
||||
#include "receiver_model.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@ -323,98 +315,6 @@ public:
|
||||
void on_focus() override;
|
||||
};
|
||||
|
||||
constexpr Style style_options_group {
|
||||
.font = font::fixed_8x16,
|
||||
.background = Color::blue(),
|
||||
.foreground = Color::white(),
|
||||
};
|
||||
|
||||
class ReceiverView : public View {
|
||||
public:
|
||||
ReceiverView(NavigationView& nav);
|
||||
~ReceiverView();
|
||||
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
static constexpr ui::Dim header_height = 2 * 16;
|
||||
|
||||
RSSI rssi {
|
||||
{ 21 * 8, 0, 6 * 8, 4 },
|
||||
};
|
||||
|
||||
Channel channel {
|
||||
{ 21 * 8, 5, 6 * 8, 4 },
|
||||
};
|
||||
|
||||
Audio audio {
|
||||
{ 21 * 8, 10, 6 * 8, 4 },
|
||||
};
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 5 * 8, 0 * 16 },
|
||||
};
|
||||
|
||||
LNAGainField field_lna {
|
||||
{ 15 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
NumberField field_vga {
|
||||
{ 18 * 8, 0 * 16},
|
||||
2,
|
||||
{ max2837::vga::gain_db_range.minimum, max2837::vga::gain_db_range.maximum },
|
||||
max2837::vga::gain_db_step,
|
||||
' ',
|
||||
};
|
||||
|
||||
OptionsField options_modulation {
|
||||
{ 0 * 8, 0 * 16 },
|
||||
4,
|
||||
{
|
||||
{ " AM ", toUType(ReceiverModel::Mode::AMAudio) },
|
||||
{ "NFM ", toUType(ReceiverModel::Mode::NarrowbandFMAudio) },
|
||||
{ "WFM ", toUType(ReceiverModel::Mode::WidebandFMAudio) },
|
||||
{ "SPEC", toUType(ReceiverModel::Mode::SpectrumAnalysis) },
|
||||
}
|
||||
};
|
||||
|
||||
NumberField field_volume {
|
||||
{ 28 * 8, 0 * 16 },
|
||||
2,
|
||||
{ 0, 99 },
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
FrequencyOptionsView view_frequency_options {
|
||||
{ 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
|
||||
&style_options_group
|
||||
};
|
||||
|
||||
RadioGainOptionsView view_rf_gain_options {
|
||||
{ 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
|
||||
&style_options_group
|
||||
};
|
||||
|
||||
std::unique_ptr<Widget> widget_content;
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
|
||||
void on_rf_amp_changed(bool v);
|
||||
void on_lna_changed(int32_t v_db);
|
||||
void on_vga_changed(int32_t v_db);
|
||||
void on_modulation_changed(ReceiverModel::Mode mode);
|
||||
void on_show_options_frequency();
|
||||
void on_show_options_rf_gain();
|
||||
void on_frequency_step_changed(rf::Frequency f);
|
||||
void on_reference_ppm_correction_changed(int32_t v);
|
||||
void on_headphone_volume_changed(int32_t v);
|
||||
void on_edit_frequency();
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_RECEIVER_H__*/
|
||||
|
@ -28,12 +28,93 @@ namespace ui {
|
||||
|
||||
/* SDCardStatusView *****************************************************/
|
||||
|
||||
SDCardStatusView::SDCardStatusView() {
|
||||
add_children({ {
|
||||
&text_status,
|
||||
} });
|
||||
namespace detail {
|
||||
|
||||
on_status(sd_card::status());
|
||||
static constexpr uint8_t bitmap_sd_card_ok_data[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f,
|
||||
0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
|
||||
0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
|
||||
0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static constexpr Bitmap bitmap_sd_card_ok {
|
||||
{ 16, 16 }, bitmap_sd_card_ok_data
|
||||
};
|
||||
|
||||
static constexpr uint8_t bitmap_sd_card_unknown_data[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f,
|
||||
0x38, 0x1c, 0x98, 0x19, 0xf8, 0x19, 0xf8, 0x1c,
|
||||
0x78, 0x1e, 0x78, 0x1e, 0xf8, 0x1f, 0x78, 0x1e,
|
||||
0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static constexpr Bitmap bitmap_sd_card_unknown {
|
||||
{ 16, 16 }, bitmap_sd_card_unknown_data
|
||||
};
|
||||
|
||||
static constexpr uint8_t bitmap_sd_card_error_data[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f,
|
||||
0xf8, 0x1f, 0xf8, 0x1b, 0xf8, 0x19, 0xf8, 0x1c,
|
||||
0xf8, 0x1e, 0xf8, 0x1c, 0xf8, 0x19, 0xf8, 0x1b,
|
||||
0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
static constexpr Bitmap bitmap_sd_card_error {
|
||||
{ 16, 16 }, bitmap_sd_card_error_data
|
||||
};
|
||||
|
||||
const Bitmap& bitmap_sd_card(const sd_card::Status status) {
|
||||
switch(status) {
|
||||
case sd_card::Status::IOError:
|
||||
case sd_card::Status::MountError:
|
||||
case sd_card::Status::ConnectError:
|
||||
return bitmap_sd_card_error;
|
||||
|
||||
case sd_card::Status::NotPresent:
|
||||
return bitmap_sd_card_unknown;
|
||||
|
||||
case sd_card::Status::Present:
|
||||
return bitmap_sd_card_unknown;
|
||||
|
||||
case sd_card::Status::Mounted:
|
||||
return bitmap_sd_card_ok;
|
||||
|
||||
default:
|
||||
return bitmap_sd_card_unknown;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr Color color_sd_card_error = Color::red();
|
||||
static constexpr Color color_sd_card_unknown = Color::yellow();
|
||||
static constexpr Color color_sd_card_ok = Color::green();
|
||||
|
||||
const Color color_sd_card(const sd_card::Status status) {
|
||||
switch(status) {
|
||||
case sd_card::Status::IOError:
|
||||
case sd_card::Status::MountError:
|
||||
case sd_card::Status::ConnectError:
|
||||
return color_sd_card_error;
|
||||
|
||||
case sd_card::Status::NotPresent:
|
||||
return color_sd_card_unknown;
|
||||
|
||||
case sd_card::Status::Present:
|
||||
return color_sd_card_unknown;
|
||||
|
||||
case sd_card::Status::Mounted:
|
||||
return color_sd_card_ok;
|
||||
|
||||
default:
|
||||
return color_sd_card_unknown;
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace detail */
|
||||
|
||||
SDCardStatusView::SDCardStatusView(
|
||||
const Rect parent_rect
|
||||
) : Image { parent_rect, &detail::bitmap_sd_card_unknown, detail::color_sd_card_unknown, Color::black() }
|
||||
{
|
||||
}
|
||||
|
||||
void SDCardStatusView::on_show() {
|
||||
@ -46,40 +127,17 @@ void SDCardStatusView::on_hide() {
|
||||
sd_card::status_signal -= sd_card_status_signal_token;
|
||||
}
|
||||
|
||||
void SDCardStatusView::on_status(const sd_card::Status status) {
|
||||
std::string msg("??");
|
||||
void SDCardStatusView::paint(Painter& painter) {
|
||||
const auto status = sd_card::status();
|
||||
set_bitmap(&detail::bitmap_sd_card(status));
|
||||
set_foreground(detail::color_sd_card(status));
|
||||
|
||||
switch(status) {
|
||||
case sd_card::Status::IOError:
|
||||
msg = "IO";
|
||||
break;
|
||||
Image::paint(painter);
|
||||
}
|
||||
|
||||
case sd_card::Status::MountError:
|
||||
msg = "MT";
|
||||
break;
|
||||
|
||||
case sd_card::Status::ConnectError:
|
||||
msg = "CN";
|
||||
break;
|
||||
|
||||
case sd_card::Status::NotPresent:
|
||||
msg = "XX";
|
||||
break;
|
||||
|
||||
case sd_card::Status::Present:
|
||||
msg = "OO";
|
||||
break;
|
||||
|
||||
case sd_card::Status::Mounted:
|
||||
msg = "OK";
|
||||
break;
|
||||
|
||||
default:
|
||||
msg = "--";
|
||||
break;
|
||||
}
|
||||
|
||||
text_status.set(msg);
|
||||
void SDCardStatusView::on_status(const sd_card::Status) {
|
||||
// Don't update image properties here, they might change. Wait until paint.
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
@ -27,19 +27,16 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
class SDCardStatusView : public View {
|
||||
class SDCardStatusView : public Image {
|
||||
public:
|
||||
SDCardStatusView();
|
||||
|
||||
SDCardStatusView(const Rect parent_rect);
|
||||
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
private:
|
||||
Text text_status {
|
||||
{ 0 * 8, 0, 2 * 8, 1 * 16 },
|
||||
"",
|
||||
};
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
private:
|
||||
SignalToken sd_card_status_signal_token;
|
||||
|
||||
void on_status(const sd_card::Status status);
|
||||
|
@ -170,7 +170,19 @@ void AntennaBiasSetupView::focus() {
|
||||
button_done.focus();
|
||||
}
|
||||
|
||||
void SetTouchCalibView::focus() {
|
||||
AboutView::AboutView(NavigationView& nav) {
|
||||
add_children({ {
|
||||
&text_title,
|
||||
&text_firmware,
|
||||
&text_cpld_hackrf,
|
||||
&text_cpld_portapack,
|
||||
&button_ok,
|
||||
} });
|
||||
|
||||
button_ok.on_select = [&nav](Button&){ nav.pop(); };
|
||||
}
|
||||
|
||||
void AboutView::focus() {
|
||||
button_ok.focus();
|
||||
}
|
||||
|
||||
|
@ -84,7 +84,7 @@ public:
|
||||
private:
|
||||
WaterfallView waterfall_view;
|
||||
FrequencyScale frequency_scale;
|
||||
ChannelSpectrumFIFO* fifo;
|
||||
ChannelSpectrumFIFO* fifo { nullptr };
|
||||
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
};
|
||||
|
@ -1,205 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __DSP_FIR_TAPS_H__
|
||||
#define __DSP_FIR_TAPS_H__
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
|
||||
#include "complex.hpp"
|
||||
|
||||
template<size_t N>
|
||||
struct fir_taps_real {
|
||||
float pass_frequency_normalized;
|
||||
float stop_frequency_normalized;
|
||||
std::array<int16_t, N> taps;
|
||||
};
|
||||
|
||||
/* 3kHz/6.7kHz @ 96kHz. sum(abs(taps)): 89429 */
|
||||
constexpr fir_taps_real<64> taps_64_lp_031_070_tfilter {
|
||||
.pass_frequency_normalized = 0.031f,
|
||||
.stop_frequency_normalized = 0.070f,
|
||||
.taps = { {
|
||||
56, 58, 81, 100, 113, 112, 92, 49,
|
||||
-21, -120, -244, -389, -543, -692, -819, -903,
|
||||
-923, -861, -698, -424, -34, 469, 1073, 1756,
|
||||
2492, 3243, 3972, 4639, 5204, 5634, 5903, 5995,
|
||||
5903, 5634, 5204, 4639, 3972, 3243, 2492, 1756,
|
||||
1073, 469, -34, -424, -698, -861, -923, -903,
|
||||
-819, -692, -543, -389, -244, -120, -21, 49,
|
||||
92, 112, 113, 100, 81, 58, 56, 0,
|
||||
} },
|
||||
};
|
||||
|
||||
/* 4kHz/7.5kHz @ 96kHz. sum(abs(taps)): 96783 */
|
||||
constexpr fir_taps_real<64> taps_64_lp_042_078_tfilter {
|
||||
.pass_frequency_normalized = 0.042f,
|
||||
.stop_frequency_normalized = 0.078f,
|
||||
.taps = { {
|
||||
-19, 39, 72, 126, 197, 278, 360, 432,
|
||||
478, 485, 438, 327, 152, -82, -359, -651,
|
||||
-922, -1132, -1236, -1192, -968, -545, 81, 892,
|
||||
1852, 2906, 3984, 5012, 5910, 6609, 7053, 7205,
|
||||
7053, 6609, 5910, 5012, 3984, 2906, 1852, 892,
|
||||
81, -545, -968, -1192, -1236, -1132, -922, -651,
|
||||
-359, -82, 152, 327, 438, 485, 478, 432,
|
||||
360, 278, 197, 126, 72, 39, -19, 0,
|
||||
} },
|
||||
};
|
||||
|
||||
/* 5kHz/8.5kHz @ 96kHz. sum(abs(taps)): 101312 */
|
||||
constexpr fir_taps_real<64> taps_64_lp_052_089_tfilter {
|
||||
.pass_frequency_normalized = 0.052f,
|
||||
.stop_frequency_normalized = 0.089f,
|
||||
.taps = { {
|
||||
-65, -88, -129, -163, -178, -160, -100, 9,
|
||||
160, 340, 523, 675, 758, 738, 591, 313,
|
||||
-76, -533, -987, -1355, -1544, -1472, -1077, -335,
|
||||
738, 2078, 3579, 5104, 6502, 7627, 8355, 8608,
|
||||
8355, 7627, 6502, 5104, 3579, 2078, 738, -335,
|
||||
-1077, -1472, -1544, -1355, -987, -533, -76, 313,
|
||||
591, 738, 758, 675, 523, 340, 160, 9,
|
||||
-100, -160, -178, -163, -129, -88, -65, 0,
|
||||
} },
|
||||
};
|
||||
|
||||
/* 6kHz/9.6kHz @ 96kHz. sum(abs(taps)): 105088 */
|
||||
constexpr fir_taps_real<64> taps_64_lp_063_100_tfilter {
|
||||
.pass_frequency_normalized = 0.063f,
|
||||
.stop_frequency_normalized = 0.100f,
|
||||
.taps = { {
|
||||
43, 21, -2, -54, -138, -245, -360, -453,
|
||||
-493, -451, -309, -73, 227, 535, 776, 876,
|
||||
773, 443, -86, -730, -1357, -1801, -1898, -1515,
|
||||
-585, 869, 2729, 4794, 6805, 8490, 9611, 10004,
|
||||
9611, 8490, 6805, 4794, 2729, 869, -585, -1515,
|
||||
-1898, -1801, -1357, -730, -86, 443, 773, 876,
|
||||
776, 535, 227, -73, -309, -451, -493, -453,
|
||||
-360, -245, -138, -54, -2, 21, 43, 0,
|
||||
} },
|
||||
};
|
||||
|
||||
/* 7kHz/10.4kHz @ 96kHz: sum(abs(taps)): 110157 */
|
||||
constexpr fir_taps_real<64> taps_64_lp_073_108_tfilter {
|
||||
.pass_frequency_normalized = 0.073f,
|
||||
.stop_frequency_normalized = 0.108f,
|
||||
.taps = { {
|
||||
79, 145, 241, 334, 396, 394, 306, 130,
|
||||
-109, -360, -550, -611, -494, -197, 229, 677,
|
||||
1011, 1096, 846, 257, -570, -1436, -2078, -2225,
|
||||
-1670, -327, 1726, 4245, 6861, 9146, 10704, 11257,
|
||||
10704, 9146, 6861, 4245, 1726, -327, -1670, -2225,
|
||||
-2078, -1436, -570, 257, 846, 1096, 1011, 677,
|
||||
229, -197, -494, -611, -550, -360, -109, 130,
|
||||
306, 394, 396, 334, 241, 145, 79, 0,
|
||||
} },
|
||||
};
|
||||
|
||||
/* 8kHz/11.5kHz @ 96kHz. sum(abs(taps)): 112092 */
|
||||
constexpr fir_taps_real<64> taps_64_lp_083_120_tfilter {
|
||||
.pass_frequency_normalized = 0.083f,
|
||||
.stop_frequency_normalized = 0.120f,
|
||||
.taps = { {
|
||||
-63, -72, -71, -21, 89, 248, 417, 537,
|
||||
548, 407, 124, -237, -563, -723, -621, -238,
|
||||
337, 919, 1274, 1201, 617, -382, -1514, -2364,
|
||||
-2499, -1600, 414, 3328, 6651, 9727, 11899, 12682,
|
||||
11899, 9727, 6651, 3328, 414, -1600, -2499, -2364,
|
||||
-1514, -382, 617, 1201, 1274, 919, 337, -238,
|
||||
-621, -723, -563, -237, 124, 407, 548, 537,
|
||||
417, 248, 89, -21, -71, -72, -63, 0,
|
||||
} },
|
||||
};
|
||||
|
||||
/* 9kHz/12.4kHz @ 96kHz. sum(abs(taps)): 116249 */
|
||||
constexpr fir_taps_real<64> taps_64_lp_094_129_tfilter {
|
||||
.pass_frequency_normalized = 0.094f,
|
||||
.stop_frequency_normalized = 0.129f,
|
||||
.taps = { {
|
||||
5, -93, -198, -335, -449, -478, -378, -144,
|
||||
166, 444, 563, 440, 82, -395, -788, -892,
|
||||
-589, 73, 859, 1421, 1431, 734, -530, -1919,
|
||||
-2798, -2555, -837, 2274, 6220, 10103, 12941, 13981,
|
||||
12941, 10103, 6220, 2274, -837, -2555, -2798, -1919,
|
||||
-530, 734, 1431, 1421, 859, 73, -589, -892,
|
||||
-788, -395, 82, 440, 563, 444, 166, -144,
|
||||
-378, -478, -449, -335, -198, -93, 5, 0,
|
||||
} },
|
||||
};
|
||||
|
||||
/* 10kHz/13.4kHz @ 96kHz. sum(abs(taps)): 118511 */
|
||||
constexpr fir_taps_real<64> taps_64_lp_104_140_tfilter {
|
||||
.pass_frequency_normalized = 0.104f,
|
||||
.stop_frequency_normalized = 0.140f,
|
||||
.taps = { {
|
||||
89, 159, 220, 208, 84, -147, -412, -597,
|
||||
-588, -345, 58, 441, 595, 391, -128, -730,
|
||||
-1080, -914, -198, 793, 1558, 1594, 678, -942,
|
||||
-2546, -3187, -2084, 992, 5515, 10321, 13985, 15353,
|
||||
13985, 10321, 5515, 992, -2084, -3187, -2546, -942,
|
||||
678, 1594, 1558, 793, -198, -914, -1080, -730,
|
||||
-128, 391, 595, 441, 58, -345, -588, -597,
|
||||
-412, -147, 84, 208, 220, 159, 89, 0,
|
||||
} },
|
||||
};
|
||||
|
||||
/* Wideband FM channel filter
|
||||
* 103kHz/128kHz @ 768kHz
|
||||
*/
|
||||
constexpr fir_taps_real<64> taps_64_lp_130_169_tfilter {
|
||||
.pass_frequency_normalized = 0.130f,
|
||||
.stop_frequency_normalized = 0.169f,
|
||||
.taps = { {
|
||||
100, 127, 62, -157, -470, -707, -678, -332,
|
||||
165, 494, 400, -85, -610, -729, -253, 535,
|
||||
1026, 734, -263, -1264, -1398, -332, 1316, 2259,
|
||||
1447, -988, -3474, -3769, -385, 6230, 13607, 18450,
|
||||
18450, 13607, 6230, -385, -3769, -3474, -988, 1447,
|
||||
2259, 1316, -332, -1398, -1264, -263, 734, 1026,
|
||||
535, -253, -729, -610, -85, 400, 494, 165,
|
||||
-332, -678, -707, -470, -157, 62, 127, 100,
|
||||
} },
|
||||
};
|
||||
|
||||
/* Wideband audio filter */
|
||||
/* 96kHz int16_t input
|
||||
* -> FIR filter, <15kHz (0.156fs) pass, >19kHz (0.198fs) stop
|
||||
* -> 48kHz int16_t output, gain of 1.0 (I think).
|
||||
* Padded to multiple of four taps for unrolled FIR code.
|
||||
* sum(abs(taps)): 125270
|
||||
*/
|
||||
constexpr fir_taps_real<64> taps_64_lp_156_198 {
|
||||
.pass_frequency_normalized = 0.156f,
|
||||
.stop_frequency_normalized = 0.196f,
|
||||
.taps = { {
|
||||
-27, 166, 104, -36, -174, -129, 109, 287,
|
||||
148, -232, -430, -130, 427, 597, 49, -716,
|
||||
-778, 137, 1131, 957, -493, -1740, -1121, 1167,
|
||||
2733, 1252, -2633, -4899, -1336, 8210, 18660, 23254,
|
||||
18660, 8210, -1336, -4899, -2633, 1252, 2733, 1167,
|
||||
-1121, -1740, -493, 957, 1131, 137, -778, -716,
|
||||
49, 597, 427, -130, -430, -232, 148, 287,
|
||||
109, -129, -174, -36, 104, 166, -27, 0,
|
||||
} },
|
||||
};
|
||||
|
||||
#endif/*__DSP_FIR_TAPS_H__*/
|
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "irq_ipc_m4.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
void m0apptxevent_interrupt_enable() {
|
||||
nvicEnableVector(M0CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M0APPTXEVENT_IRQ_PRIORITY));
|
||||
}
|
||||
|
||||
void m0apptxevent_interrupt_disable() {
|
||||
nvicDisableVector(M0CORE_IRQn);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
CH_IRQ_HANDLER(MAPP_IRQHandler) {
|
||||
CH_IRQ_PROLOGUE();
|
||||
|
||||
chSysLockFromIsr();
|
||||
events_flag_isr(EVT_MASK_BASEBAND);
|
||||
chSysUnlockFromIsr();
|
||||
|
||||
creg::m0apptxevent::clear();
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __IRQ_IPC_M4_H__
|
||||
#define __IRQ_IPC_M4_H__
|
||||
|
||||
void m0apptxevent_interrupt_enable();
|
||||
void m0apptxevent_interrupt_disable();
|
||||
|
||||
#endif/*__IRQ_IPC_M4_H__*/
|
@ -57,6 +57,17 @@ void AudioOutput::write(
|
||||
|
||||
void AudioOutput::write(
|
||||
const buffer_f32_t& audio
|
||||
) {
|
||||
block_buffer.feed(
|
||||
audio,
|
||||
[this](const buffer_f32_t& buffer) {
|
||||
this->on_block(buffer);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void AudioOutput::on_block(
|
||||
const buffer_f32_t& audio
|
||||
) {
|
||||
const auto audio_present_now = squelch.execute(audio);
|
||||
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_squelch.hpp"
|
||||
|
||||
#include "block_decimator.hpp"
|
||||
#include "audio_stats_collector.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
@ -43,6 +44,8 @@ public:
|
||||
void write(const buffer_f32_t& audio);
|
||||
|
||||
private:
|
||||
BlockDecimator<float, 32> block_buffer { 1 };
|
||||
|
||||
IIRBiquadFilter hpf;
|
||||
IIRBiquadFilter deemph;
|
||||
FMSquelch squelch;
|
||||
@ -51,6 +54,7 @@ private:
|
||||
|
||||
uint64_t audio_present_history = 0;
|
||||
|
||||
void on_block(const buffer_f32_t& audio);
|
||||
void fill_audio_buffer(const buffer_f32_t& audio);
|
||||
void feed_audio_stats(const buffer_f32_t& audio);
|
||||
};
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "dsp_types.hpp"
|
||||
#include "complex.hpp"
|
||||
|
||||
template<size_t N>
|
||||
template<typename T, size_t N>
|
||||
class BlockDecimator {
|
||||
public:
|
||||
constexpr BlockDecimator(
|
||||
@ -65,7 +65,7 @@ public:
|
||||
}
|
||||
|
||||
template<typename BlockCallback>
|
||||
void feed(const buffer_c16_t& src, BlockCallback callback) {
|
||||
void feed(const buffer_t<T>& src, BlockCallback callback) {
|
||||
/* NOTE: Input block size must be >= factor */
|
||||
|
||||
set_input_sampling_rate(src.sampling_rate);
|
||||
@ -85,7 +85,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<complex16_t, N> buffer;
|
||||
std::array<T, N> buffer;
|
||||
uint32_t input_sampling_rate_ { 0 };
|
||||
size_t factor_ { 1 };
|
||||
size_t src_i { 0 };
|
||||
|
@ -179,10 +179,6 @@ static inline uint32_t scale_round_and_pack(
|
||||
|
||||
// FIRC8xR16x24FS4Decim4 //////////////////////////////////////////////////
|
||||
|
||||
FIRC8xR16x24FS4Decim4::FIRC8xR16x24FS4Decim4() {
|
||||
z_.fill({});
|
||||
}
|
||||
|
||||
void FIRC8xR16x24FS4Decim4::configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale,
|
||||
@ -196,6 +192,7 @@ void FIRC8xR16x24FS4Decim4::configure(
|
||||
taps_[i+3] = taps[i+3] * negate_factor;
|
||||
}
|
||||
output_scale = scale;
|
||||
z_.fill({});
|
||||
}
|
||||
|
||||
buffer_c16_t FIRC8xR16x24FS4Decim4::execute(
|
||||
@ -244,10 +241,6 @@ buffer_c16_t FIRC8xR16x24FS4Decim4::execute(
|
||||
|
||||
// FIRC8xR16x24FS4Decim8 //////////////////////////////////////////////////
|
||||
|
||||
FIRC8xR16x24FS4Decim8::FIRC8xR16x24FS4Decim8() {
|
||||
z_.fill({});
|
||||
}
|
||||
|
||||
void FIRC8xR16x24FS4Decim8::configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale,
|
||||
@ -261,6 +254,7 @@ void FIRC8xR16x24FS4Decim8::configure(
|
||||
taps_[i+3] = taps[i+3] * negate_factor;
|
||||
}
|
||||
output_scale = scale;
|
||||
z_.fill({});
|
||||
}
|
||||
|
||||
buffer_c16_t FIRC8xR16x24FS4Decim8::execute(
|
||||
@ -309,16 +303,13 @@ buffer_c16_t FIRC8xR16x24FS4Decim8::execute(
|
||||
|
||||
// FIRC16xR16x16Decim2 ////////////////////////////////////////////////////
|
||||
|
||||
FIRC16xR16x16Decim2::FIRC16xR16x16Decim2() {
|
||||
z_.fill({});
|
||||
}
|
||||
|
||||
void FIRC16xR16x16Decim2::configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale
|
||||
) {
|
||||
std::copy(taps.cbegin(), taps.cend(), taps_.begin());
|
||||
output_scale = scale;
|
||||
z_.fill({});
|
||||
}
|
||||
|
||||
buffer_c16_t FIRC16xR16x16Decim2::execute(
|
||||
@ -363,16 +354,13 @@ buffer_c16_t FIRC16xR16x16Decim2::execute(
|
||||
|
||||
// FIRC16xR16x32Decim8 ////////////////////////////////////////////////////
|
||||
|
||||
FIRC16xR16x32Decim8::FIRC16xR16x32Decim8() {
|
||||
z_.fill({});
|
||||
}
|
||||
|
||||
void FIRC16xR16x32Decim8::configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale
|
||||
) {
|
||||
std::copy(taps.cbegin(), taps.cend(), taps_.begin());
|
||||
output_scale = scale;
|
||||
z_.fill({});
|
||||
}
|
||||
|
||||
buffer_c16_t FIRC16xR16x32Decim8::execute(
|
||||
@ -657,18 +645,6 @@ buffer_s16_t FIR64AndDecimateBy2Real::execute(
|
||||
return { dst.p, src.count / 2, src.sampling_rate / 2 };
|
||||
}
|
||||
|
||||
void FIRAndDecimateComplex::configure(
|
||||
const int16_t* const taps,
|
||||
const size_t taps_count,
|
||||
const size_t decimation_factor
|
||||
) {
|
||||
samples_ = std::make_unique<samples_t>(taps_count);
|
||||
taps_reversed_ = std::make_unique<taps_t>(taps_count);
|
||||
taps_count_ = taps_count;
|
||||
decimation_factor_ = decimation_factor;
|
||||
std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]);
|
||||
}
|
||||
|
||||
buffer_c16_t FIRAndDecimateComplex::execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst
|
||||
|
@ -103,8 +103,6 @@ public:
|
||||
Up = false
|
||||
};
|
||||
|
||||
FIRC8xR16x24FS4Decim4();
|
||||
|
||||
void configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale,
|
||||
@ -135,8 +133,6 @@ public:
|
||||
Up = false
|
||||
};
|
||||
|
||||
FIRC8xR16x24FS4Decim8();
|
||||
|
||||
void configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale,
|
||||
@ -162,8 +158,6 @@ public:
|
||||
using sample_t = complex16_t;
|
||||
using tap_t = int16_t;
|
||||
|
||||
FIRC16xR16x16Decim2();
|
||||
|
||||
void configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale
|
||||
@ -188,8 +182,6 @@ public:
|
||||
using sample_t = complex16_t;
|
||||
using tap_t = int16_t;
|
||||
|
||||
FIRC16xR16x32Decim8();
|
||||
|
||||
void configure(
|
||||
const std::array<tap_t, taps_count>& taps,
|
||||
const int32_t scale
|
||||
@ -243,11 +235,18 @@ private:
|
||||
size_t taps_count_;
|
||||
size_t decimation_factor_;
|
||||
|
||||
template<typename T>
|
||||
void configure(
|
||||
const int16_t* const taps,
|
||||
const T* const taps,
|
||||
const size_t taps_count,
|
||||
const size_t decimation_factor
|
||||
);
|
||||
) {
|
||||
samples_ = std::make_unique<samples_t>(taps_count);
|
||||
taps_reversed_ = std::make_unique<taps_t>(taps_count);
|
||||
taps_count_ = taps_count;
|
||||
decimation_factor_ = decimation_factor;
|
||||
std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]);
|
||||
}
|
||||
};
|
||||
|
||||
class DecimateBy2CIC4Real {
|
||||
|
@ -34,17 +34,10 @@ buffer_f32_t AM::execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_f32_t& dst
|
||||
) {
|
||||
/* Intermediate maximum value: 46341 (when input is -32768,-32768). */
|
||||
/* Normalized to maximum 32767 for int16_t representation. */
|
||||
|
||||
const auto src_p = src.p;
|
||||
const auto src_end = &src.p[src.count];
|
||||
auto dst_p = dst.p;
|
||||
while(src_p < src_end) {
|
||||
// const auto s = *(src_p++);
|
||||
// const uint32_t r_sq = s.real() * s.real();
|
||||
// const uint32_t i_sq = s.imag() * s.imag();
|
||||
// const uint32_t mag_sq = r_sq + i_sq;
|
||||
const uint32_t sample0 = *__SIMD32(src_p)++;
|
||||
const uint32_t sample1 = *__SIMD32(src_p)++;
|
||||
const uint32_t mag_sq0 = __SMUAD(sample0, sample0);
|
||||
@ -55,6 +48,23 @@ buffer_f32_t AM::execute(
|
||||
|
||||
return { dst.p, src.count, src.sampling_rate };
|
||||
}
|
||||
|
||||
buffer_f32_t SSB::execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_f32_t& dst
|
||||
) {
|
||||
const complex16_t* src_p = src.p;
|
||||
const auto src_end = &src.p[src.count];
|
||||
auto dst_p = dst.p;
|
||||
while(src_p < src_end) {
|
||||
*(dst_p++) = (src_p++)->real();
|
||||
*(dst_p++) = (src_p++)->real();
|
||||
*(dst_p++) = (src_p++)->real();
|
||||
*(dst_p++) = (src_p++)->real();
|
||||
}
|
||||
|
||||
return { dst.p, src.count, src.sampling_rate };
|
||||
}
|
||||
/*
|
||||
static inline float angle_approx_4deg0(const complex32_t t) {
|
||||
const auto x = static_cast<float>(t.imag()) / static_cast<float>(t.real());
|
||||
@ -89,8 +99,8 @@ buffer_f32_t FM::execute(
|
||||
const auto t0 = multiply_conjugate_s16_s32(s0, z);
|
||||
const auto t1 = multiply_conjugate_s16_s32(s1, s0);
|
||||
z = s1;
|
||||
*(dst_p++) = angle_approx_0deg27(t0) * k;
|
||||
*(dst_p++) = angle_approx_0deg27(t1) * k;
|
||||
*(dst_p++) = angle_precise(t0) * k;
|
||||
*(dst_p++) = angle_precise(t1) * k;
|
||||
}
|
||||
z_ = z;
|
||||
|
||||
|
@ -35,6 +35,14 @@ public:
|
||||
);
|
||||
};
|
||||
|
||||
class SSB {
|
||||
public:
|
||||
buffer_f32_t execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_f32_t& dst
|
||||
);
|
||||
};
|
||||
|
||||
class FM {
|
||||
public:
|
||||
buffer_f32_t execute(
|
||||
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __DSP_IIR_CONFIG_H__
|
||||
#define __DSP_IIR_CONFIG_H__
|
||||
|
||||
#include "dsp_iir.hpp"
|
||||
|
||||
// scipy.signal.butter(2, 30 / 24000.0, 'highpass', analog=False)
|
||||
constexpr iir_biquad_config_t audio_hpf_30hz_config {
|
||||
{ 0.99722705f, -1.99445410f, 0.99722705f },
|
||||
{ 1.00000000f, -1.99444641f, 0.99446179f }
|
||||
};
|
||||
|
||||
// scipy.signal.butter(2, 300 / 24000.0, 'highpass', analog=False)
|
||||
constexpr iir_biquad_config_t audio_hpf_300hz_config {
|
||||
{ 0.97261390f, -1.94522780f, 0.97261390f },
|
||||
{ 1.00000000f, -1.94447766f, 0.94597794f }
|
||||
};
|
||||
|
||||
// scipy.signal.iirdesign(wp=8000 / 24000.0, ws= 4000 / 24000.0, gpass=1, gstop=18, ftype='ellip')
|
||||
constexpr iir_biquad_config_t non_audio_hpf_config {
|
||||
{ 0.51891061f, -0.95714180f, 0.51891061f },
|
||||
{ 1.0f , -0.79878302f, 0.43960231f }
|
||||
};
|
||||
|
||||
// scipy.signal.butter(1, 300 / 24000.0, 'lowpass', analog=False)
|
||||
// NOTE: Technically, order-1 filter, b[2] = a[2] = 0.
|
||||
constexpr iir_biquad_config_t audio_deemph_300_6_config {
|
||||
{ 0.01925927f, 0.01925927f, 0.00000000f },
|
||||
{ 1.00000000f, -0.96148145f, 0.00000000f }
|
||||
};
|
||||
|
||||
// 75us RC time constant, used in broadcast FM in Americas, South Korea
|
||||
// scipy.signal.butter(1, 2122 / 24000.0, 'lowpass', analog=False)
|
||||
// NOTE: Technically, order-1 filter, b[2] = a[2] = 0.
|
||||
constexpr iir_biquad_config_t audio_deemph_2122_6_config {
|
||||
{ 0.12264116f, 0.12264116f, 0.00000000f },
|
||||
{ 1.00000000f, -0.75471767f, 0.00000000f }
|
||||
};
|
||||
|
||||
#endif/*__DSP_IIR_CONFIG_H__*/
|
@ -21,7 +21,6 @@
|
||||
|
||||
#include "proc_am_audio.hpp"
|
||||
|
||||
#include "dsp_iir_config.hpp"
|
||||
#include "audio_output.hpp"
|
||||
|
||||
#include <array>
|
||||
@ -33,17 +32,25 @@ void NarrowbandAMAudio::execute(const buffer_c8_t& 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);
|
||||
const auto decim_2_out = decim_2.execute(decim_1_out, dst_buffer);
|
||||
const auto channel_out = channel_filter.execute(decim_2_out, dst_buffer);
|
||||
|
||||
// TODO: Feed channel_stats post-decimation data?
|
||||
feed_channel_stats(channel_out);
|
||||
channel_spectrum.feed(channel_out, channel_filter_pass_f, channel_filter_stop_f);
|
||||
|
||||
auto audio = demod.execute(channel_out, work_audio_buffer);
|
||||
|
||||
auto audio = demodulate(channel_out);
|
||||
audio_output.write(audio);
|
||||
}
|
||||
|
||||
buffer_f32_t NarrowbandAMAudio::demodulate(const buffer_c16_t& channel) {
|
||||
if( modulation_ssb ) {
|
||||
return demod_ssb.execute(channel, audio_buffer);
|
||||
} else {
|
||||
return demod_am.execute(channel, audio_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
void NarrowbandAMAudio::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
case Message::ID::UpdateSpectrum:
|
||||
@ -61,27 +68,27 @@ void NarrowbandAMAudio::on_message(const Message* const message) {
|
||||
}
|
||||
|
||||
void NarrowbandAMAudio::configure(const AMConfigureMessage& message) {
|
||||
constexpr size_t baseband_fs = 3072000;
|
||||
|
||||
constexpr size_t decim_0_input_fs = baseband_fs;
|
||||
constexpr size_t decim_0_decimation_factor = 8;
|
||||
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0_decimation_factor;
|
||||
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
|
||||
|
||||
constexpr size_t decim_1_input_fs = decim_0_output_fs;
|
||||
constexpr size_t decim_1_decimation_factor = 8;
|
||||
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1_decimation_factor;
|
||||
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
|
||||
|
||||
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
|
||||
constexpr size_t channel_filter_decimation_factor = 1;
|
||||
constexpr size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor;
|
||||
constexpr size_t decim_2_input_fs = decim_1_output_fs;
|
||||
constexpr size_t decim_2_output_fs = decim_2_input_fs / decim_2_decimation_factor;
|
||||
|
||||
constexpr size_t channel_filter_input_fs = decim_2_output_fs;
|
||||
const size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor;
|
||||
|
||||
decim_0.configure(message.decim_0_filter.taps, 33554432);
|
||||
decim_1.configure(message.decim_1_filter.taps, 131072);
|
||||
decim_2.configure(message.decim_2_filter.taps, decim_2_decimation_factor);
|
||||
channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor);
|
||||
channel_filter_pass_f = message.channel_filter.pass_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_stop_f = message.channel_filter.stop_frequency_normalized * channel_filter_input_fs;
|
||||
channel_spectrum.set_decimation_factor(std::floor((channel_filter_output_fs / 2) / ((channel_filter_pass_f + channel_filter_stop_f) / 2)));
|
||||
audio_output.configure(audio_hpf_300hz_config);
|
||||
modulation_ssb = (message.modulation == AMConfigureMessage::Modulation::SSB);
|
||||
audio_output.configure(message.audio_hpf_config);
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
@ -39,23 +39,31 @@ public:
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 3072000;
|
||||
static constexpr size_t decim_2_decimation_factor = 4;
|
||||
static constexpr size_t channel_filter_decimation_factor = 1;
|
||||
|
||||
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)
|
||||
std::array<float, 32> audio;
|
||||
const buffer_f32_t audio_buffer {
|
||||
audio.data(),
|
||||
audio.size()
|
||||
};
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0;
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1;
|
||||
dsp::decimate::FIRAndDecimateComplex decim_2;
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter;
|
||||
uint32_t channel_filter_pass_f;
|
||||
uint32_t channel_filter_stop_f;
|
||||
uint32_t channel_filter_pass_f = 0;
|
||||
uint32_t channel_filter_stop_f = 0;
|
||||
|
||||
dsp::demodulate::AM demod;
|
||||
bool modulation_ssb = false;
|
||||
dsp::demodulate::AM demod_am;
|
||||
dsp::demodulate::SSB demod_ssb;
|
||||
|
||||
AudioOutput audio_output;
|
||||
|
||||
@ -63,6 +71,8 @@ private:
|
||||
|
||||
bool configured { false };
|
||||
void configure(const AMConfigureMessage& message);
|
||||
|
||||
buffer_f32_t demodulate(const buffer_c16_t& channel);
|
||||
};
|
||||
|
||||
#endif/*__PROC_AM_AUDIO_H__*/
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
#include "proc_nfm_audio.hpp"
|
||||
|
||||
#include "dsp_iir_config.hpp"
|
||||
#include "audio_output.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
@ -39,8 +38,7 @@ void NarrowbandFMAudio::execute(const buffer_c8_t& buffer) {
|
||||
feed_channel_stats(channel_out);
|
||||
channel_spectrum.feed(channel_out, channel_filter_pass_f, channel_filter_stop_f);
|
||||
|
||||
auto audio = demod.execute(channel_out, work_audio_buffer);
|
||||
|
||||
auto audio = demod.execute(channel_out, audio_buffer);
|
||||
audio_output.write(audio);
|
||||
}
|
||||
|
||||
@ -61,30 +59,25 @@ void NarrowbandFMAudio::on_message(const Message* const message) {
|
||||
}
|
||||
|
||||
void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) {
|
||||
constexpr size_t baseband_fs = 3072000;
|
||||
|
||||
constexpr size_t decim_0_input_fs = baseband_fs;
|
||||
constexpr size_t decim_0_decimation_factor = 8;
|
||||
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0_decimation_factor;
|
||||
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
|
||||
|
||||
constexpr size_t decim_1_input_fs = decim_0_output_fs;
|
||||
constexpr size_t decim_1_decimation_factor = 8;
|
||||
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1_decimation_factor;
|
||||
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
|
||||
|
||||
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
|
||||
constexpr size_t channel_filter_decimation_factor = 1;
|
||||
constexpr size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor;
|
||||
const size_t channel_filter_output_fs = channel_filter_input_fs / message.channel_decimation;
|
||||
|
||||
constexpr size_t demod_input_fs = channel_filter_output_fs;
|
||||
const size_t demod_input_fs = channel_filter_output_fs;
|
||||
|
||||
decim_0.configure(message.decim_0_filter.taps, 33554432);
|
||||
decim_1.configure(message.decim_1_filter.taps, 131072);
|
||||
channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor);
|
||||
channel_filter.configure(message.channel_filter.taps, message.channel_decimation);
|
||||
demod.configure(demod_input_fs, message.deviation);
|
||||
channel_filter_pass_f = message.channel_filter.pass_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_stop_f = message.channel_filter.stop_frequency_normalized * channel_filter_input_fs;
|
||||
channel_spectrum.set_decimation_factor(std::floor((channel_filter_output_fs / 2) / ((channel_filter_pass_f + channel_filter_stop_f) / 2)));
|
||||
audio_output.configure(audio_hpf_300hz_config, audio_deemph_300_6_config, 6144);
|
||||
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, 12288);
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
@ -24,12 +24,15 @@
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
|
||||
#include "channel_decimator.hpp"
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
#include "spectrum_collector.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class NarrowbandFMAudio : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
@ -37,19 +40,21 @@ public:
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 3072000;
|
||||
|
||||
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)
|
||||
std::array<float, 32> audio;
|
||||
const buffer_f32_t audio_buffer {
|
||||
audio.data(),
|
||||
audio.size()
|
||||
};
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0;
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1;
|
||||
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter;
|
||||
uint32_t channel_filter_pass_f = 0;
|
||||
uint32_t channel_filter_stop_f = 0;
|
||||
|
@ -21,7 +21,6 @@
|
||||
|
||||
#include "proc_wfm_audio.hpp"
|
||||
|
||||
#include "dsp_iir_config.hpp"
|
||||
#include "audio_output.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
@ -88,19 +87,14 @@ void WidebandFMAudio::on_message(const Message* const message) {
|
||||
}
|
||||
|
||||
void WidebandFMAudio::configure(const WFMConfigureMessage& message) {
|
||||
constexpr size_t baseband_fs = 3072000;
|
||||
|
||||
constexpr size_t decim_0_input_fs = baseband_fs;
|
||||
constexpr size_t decim_0_decimation_factor = 4;
|
||||
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0_decimation_factor;
|
||||
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
|
||||
|
||||
constexpr size_t decim_1_input_fs = decim_0_output_fs;
|
||||
constexpr size_t decim_1_decimation_factor = 2;
|
||||
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1_decimation_factor;
|
||||
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
|
||||
|
||||
constexpr size_t demod_input_fs = decim_1_output_fs;
|
||||
|
||||
constexpr auto spectrum_rate_hz = 50.0f;
|
||||
spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz;
|
||||
spectrum_samples = 0;
|
||||
|
||||
@ -110,7 +104,7 @@ void WidebandFMAudio::configure(const WFMConfigureMessage& message) {
|
||||
channel_filter_stop_f = message.decim_1_filter.stop_frequency_normalized * decim_1_input_fs;
|
||||
demod.configure(demod_input_fs, message.deviation);
|
||||
audio_filter.configure(message.audio_filter.taps);
|
||||
audio_output.configure(audio_hpf_30hz_config, audio_deemph_2122_6_config);
|
||||
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config);
|
||||
|
||||
channel_spectrum.set_decimation_factor(1);
|
||||
|
||||
|
@ -37,6 +37,9 @@ public:
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 3072000;
|
||||
static constexpr auto spectrum_rate_hz = 50.0f;
|
||||
|
||||
std::array<complex16_t, 512> dst;
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
|
@ -50,7 +50,7 @@ public:
|
||||
);
|
||||
|
||||
private:
|
||||
BlockDecimator<256> channel_spectrum_decimator;
|
||||
BlockDecimator<complex16_t, 256> channel_spectrum_decimator;
|
||||
ChannelSpectrumFIFO fifo;
|
||||
|
||||
volatile bool channel_spectrum_request_update { false };
|
||||
|
@ -34,6 +34,13 @@ struct fir_taps_real {
|
||||
std::array<int16_t, N> taps;
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
struct fir_taps_complex {
|
||||
float pass_frequency_normalized;
|
||||
float stop_frequency_normalized;
|
||||
std::array<complex16_t, N> taps;
|
||||
};
|
||||
|
||||
// NBFM 16K0F3E emission type /////////////////////////////////////////////
|
||||
|
||||
// IFIR image-reject filter: fs=3072000, pass=8000, stop=344000, decim=8, fout=384000
|
||||
@ -170,8 +177,8 @@ constexpr fir_taps_real<32> taps_6k0_decim_1 {
|
||||
} },
|
||||
};
|
||||
|
||||
// Channel filter: fs=48000, pass=3000, stop=6700, decim=1, fout=48000
|
||||
constexpr fir_taps_real<32> taps_6k0_channel {
|
||||
// IFIR prototype filter: fs=48000, pass=3000, stop=6700, decim=4, fout=12000
|
||||
constexpr fir_taps_real<32> taps_6k0_decim_2 {
|
||||
.pass_frequency_normalized = 3000.0f / 48000.0f,
|
||||
.stop_frequency_normalized = 6700.0f / 48000.0f,
|
||||
.taps = { {
|
||||
@ -182,6 +189,85 @@ constexpr fir_taps_real<32> taps_6k0_channel {
|
||||
} },
|
||||
};
|
||||
|
||||
// Channel filter: fs=12000, pass=3000, stop=3300, decim=1, fout=12000
|
||||
/* NOTE: Slightly less than 1.0 gain (normalized to 65536) due to max(taps) being
|
||||
* slightly larger than 32767 (33312).
|
||||
*/
|
||||
constexpr fir_taps_complex<64> taps_6k0_dsb_channel {
|
||||
.pass_frequency_normalized = 3000.0f / 12000.0f,
|
||||
.stop_frequency_normalized = 3300.0f / 12000.0f,
|
||||
.taps = { {
|
||||
{ -69, 0 }, { -140, 0 }, { 119, 0 }, { 89, 0 },
|
||||
{ -132, 0 }, { -134, 0 }, { 197, 0 }, { 167, 0 },
|
||||
{ -273, 0 }, { -206, 0 }, { 372, 0 }, { 247, 0 },
|
||||
{ -497, 0 }, { -289, 0 }, { 654, 0 }, { 331, 0 },
|
||||
{ -854, 0 }, { -372, 0 }, { 1112, 0 }, { 411, 0 },
|
||||
{ -1455, 0 }, { -446, 0 }, { 1933, 0 }, { 476, 0 },
|
||||
{ -2654, 0 }, { -501, 0 }, { 3902, 0 }, { 520, 0 },
|
||||
{ -6717, 0 }, { -531, 0 }, { 20478, 0 }, { 32767, 0 },
|
||||
{ 20478, 0 }, { -531, 0 }, { -6717, 0 }, { 520, 0 },
|
||||
{ 3902, 0 }, { -501, 0 }, { -2654, 0 }, { 476, 0 },
|
||||
{ 1933, 0 }, { -446, 0 }, { -1455, 0 }, { 411, 0 },
|
||||
{ 1112, 0 }, { -372, 0 }, { -854, 0 }, { 331, 0 },
|
||||
{ 654, 0 }, { -289, 0 }, { -497, 0 }, { 247, 0 },
|
||||
{ 372, 0 }, { -206, 0 }, { -273, 0 }, { 167, 0 },
|
||||
{ 197, 0 }, { -134, 0 }, { -132, 0 }, { 89, 0 },
|
||||
{ 119, 0 }, { -140, 0 }, { -69, 0 }, { 0, 0 },
|
||||
} },
|
||||
};
|
||||
|
||||
// USB AM 2K80J3E emission type ///////////////////////////////////////////
|
||||
|
||||
// IFIR prototype filter: fs=12000, pass=3000, stop=3300, decim=1, fout=12000
|
||||
constexpr fir_taps_complex<64> taps_2k8_usb_channel {
|
||||
.pass_frequency_normalized = 3000.0f / 12000.0f,
|
||||
.stop_frequency_normalized = 3300.0f / 12000.0f,
|
||||
.taps = { {
|
||||
{ -146, 0 }, { -41, -45 }, { -1, 10 }, { -95, 69 },
|
||||
{ -194, -41 }, { -91, -158 }, { 14, -43 }, { -150, 67 },
|
||||
{ -299, -133 }, { -100, -307 }, { 50, -86 }, { -254, 54 },
|
||||
{ -453, -329 }, { -62, -587 }, { 170, -189 }, { -334, 0 },
|
||||
{ -580, -645 }, { 104, -986 }, { 418, -304 }, { -412, -88 },
|
||||
{ -680, -1178 }, { 527, -1623 }, { 970, -432 }, { -441, -196 },
|
||||
{ -698, -2149 }, { 1617, -2800 }, { 2384, -507 }, { -429, -311 },
|
||||
{ -545, -5181 }, { 6925, -7691 }, { 14340, 0 }, { 10601, 11773 },
|
||||
{ -1499, 14261 }, { -8373, 6083 }, { -5095, -1083 }, { -265, -459 },
|
||||
{ -753, 2318 }, { -2954, 1315 }, { -2064, -919 }, { -149, -459 },
|
||||
{ -531, 920 }, { -1669, 355 }, { -1100, -800 }, { -44, -419 },
|
||||
{ -346, 384 }, { -992, 0 }, { -580, -645 }, { 35, -332 },
|
||||
{ -205, 149 }, { -577, -123 }, { -280, -485 }, { 80, -247 },
|
||||
{ -91, 40 }, { -294, -131 }, { -101, -312 }, { 82, -142 },
|
||||
{ -44, 9 }, { -147, -107 }, { -21, -197 }, { 79, -88 },
|
||||
{ 10, 0 }, { -41, -45 }, { 15, -145 }, { 0, 0 },
|
||||
} },
|
||||
};
|
||||
|
||||
// LSB AM 2K80J3E emission type ///////////////////////////////////////////
|
||||
|
||||
// IFIR prototype filter: fs=12000, pass=3000, stop=3300, decim=1, fout=12000
|
||||
constexpr fir_taps_complex<64> taps_2k8_lsb_channel {
|
||||
.pass_frequency_normalized = 3000.0f / 12000.0f,
|
||||
.stop_frequency_normalized = 3300.0f / 12000.0f,
|
||||
.taps = { {
|
||||
{ -146, 0 }, { -41, 45 }, { -1, -10 }, { -95, -69 },
|
||||
{ -194, 41 }, { -91, 158 }, { 14, 43 }, { -150, -67 },
|
||||
{ -299, 133 }, { -100, 307 }, { 50, 86 }, { -254, -54 },
|
||||
{ -453, 329 }, { -62, 587 }, { 170, 189 }, { -334, 0 },
|
||||
{ -580, 645 }, { 104, 986 }, { 418, 304 }, { -412, 88 },
|
||||
{ -680, 1178 }, { 527, 1623 }, { 970, 432 }, { -441, 196 },
|
||||
{ -698, 2149 }, { 1617, 2800 }, { 2384, 507 }, { -429, 311 },
|
||||
{ -545, 5181 }, { 6925, 7691 }, { 14340, 0 }, { 10601, -11773 },
|
||||
{ -1499, -14261 }, { -8373, -6083 }, { -5095, 1083 }, { -265, 459 },
|
||||
{ -753, -2318 }, { -2954, -1315 }, { -2064, 919 }, { -149, 459 },
|
||||
{ -531, -920 }, { -1669, -355 }, { -1100, 800 }, { -44, 419 },
|
||||
{ -346, -384 }, { -992, 0 }, { -580, 645 }, { 35, 332 },
|
||||
{ -205, -149 }, { -577, 123 }, { -280, 485 }, { 80, 247 },
|
||||
{ -91, -40 }, { -294, 131 }, { -101, 312 }, { 82, 142 },
|
||||
{ -44, -9 }, { -147, 107 }, { -21, 197 }, { 79, 88 },
|
||||
{ 10, 0 }, { -41, 45 }, { 15, 145 }, { 0, 0 },
|
||||
} },
|
||||
};
|
||||
|
||||
// WFM 200KF8E emission type //////////////////////////////////////////////
|
||||
|
||||
// IFIR image-reject filter: fs=3072000, pass=100000, stop=484000, decim=4, fout=768000
|
||||
|
@ -19,4 +19,39 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#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);
|
||||
}
|
112
firmware/common/dsp_iir_config.hpp
Normal file
112
firmware/common/dsp_iir_config.hpp
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __DSP_IIR_CONFIG_H__
|
||||
#define __DSP_IIR_CONFIG_H__
|
||||
|
||||
#include "dsp_iir.hpp"
|
||||
|
||||
// scipy.signal.butter(2, 30 / 24000.0, 'highpass', analog=False)
|
||||
constexpr iir_biquad_config_t audio_48k_hpf_30hz_config {
|
||||
{ 0.99722705f, -1.99445410f, 0.99722705f },
|
||||
{ 1.00000000f, -1.99444641f, 0.99446179f }
|
||||
};
|
||||
|
||||
// scipy.signal.butter(2, 300 / 24000.0, 'highpass', analog=False)
|
||||
constexpr iir_biquad_config_t audio_48k_hpf_300hz_config {
|
||||
{ 0.97261390f, -1.94522780f, 0.97261390f },
|
||||
{ 1.00000000f, -1.94447766f, 0.94597794f }
|
||||
};
|
||||
|
||||
// scipy.signal.butter(2, 300 / 12000.0, 'highpass', analog=False)
|
||||
constexpr iir_biquad_config_t audio_24k_hpf_300hz_config {
|
||||
{ 0.94597686f, -1.89195371f, 0.94597686f },
|
||||
{ 1.00000000f, -1.88903308f, 0.89487434f }
|
||||
};
|
||||
|
||||
// scipy.signal.butter(2, 300 / 8000.0, 'highpass', analog=False)
|
||||
constexpr iir_biquad_config_t audio_16k_hpf_300hz_config {
|
||||
{ 0.92006616f, -1.84013232f, 0.92006616f },
|
||||
{ 1.00000000f, -1.83373266f, 0.84653197f }
|
||||
};
|
||||
|
||||
// scipy.signal.butter(2, 300 / 6000.0, 'highpass', analog=False)
|
||||
constexpr iir_biquad_config_t audio_12k_hpf_300hz_config {
|
||||
{ 0.89485861f, -1.78971721f, 0.89485861f },
|
||||
{ 1.00000000f, -1.77863178f, 0.80080265f }
|
||||
};
|
||||
|
||||
// scipy.signal.butter(2, 300 / 4000.0, 'highpass', analog=False)
|
||||
constexpr iir_biquad_config_t audio_8k_hpf_300hz_config {
|
||||
{ 0.84645925f, -1.69291851f, 0.84645925f },
|
||||
{ 1.00000000f, -1.66920314f, 0.71663387f }
|
||||
};
|
||||
|
||||
// scipy.signal.iirdesign(wp=8000 / 24000.0, ws= 4000 / 24000.0, gpass=1, gstop=18, ftype='ellip')
|
||||
constexpr iir_biquad_config_t non_audio_hpf_config {
|
||||
{ 0.51891061f, -0.95714180f, 0.51891061f },
|
||||
{ 1.0f , -0.79878302f, 0.43960231f }
|
||||
};
|
||||
|
||||
// scipy.signal.butter(1, 300 / 24000.0, 'lowpass', analog=False)
|
||||
// NOTE: Technically, order-1 filter, b[2] = a[2] = 0.
|
||||
constexpr iir_biquad_config_t audio_48k_deemph_300_6_config {
|
||||
{ 0.01925927f, 0.01925927f, 0.00000000f },
|
||||
{ 1.00000000f, -0.96148145f, 0.00000000f }
|
||||
};
|
||||
|
||||
// scipy.signal.butter(1, 300 / 12000.0, 'lowpass', analog=False)
|
||||
// NOTE: Technically, order-1 filter, b[2] = a[2] = 0.
|
||||
constexpr iir_biquad_config_t audio_24k_deemph_300_6_config {
|
||||
{ 0.03780475f, 0.03780475f, 0.00000000f },
|
||||
{ 1.00000000f, -0.92439049f, 0.00000000f }
|
||||
};
|
||||
|
||||
// scipy.signal.butter(1, 300 / 8000.0, 'lowpass', analog=False)
|
||||
// NOTE: Technically, order-1 filter, b[2] = a[2] = 0.
|
||||
constexpr iir_biquad_config_t audio_16k_deemph_300_6_config {
|
||||
{ 0.05568894f, 0.05568894f, 0.00000000f },
|
||||
{ 1.00000000f, -0.88862213f, 0.00000000f }
|
||||
};
|
||||
|
||||
// scipy.signal.butter(1, 300 / 6000.0, 'lowpass', analog=False)
|
||||
// NOTE: Technically, order-1 filter, b[2] = a[2] = 0.
|
||||
constexpr iir_biquad_config_t audio_12k_deemph_300_6_config {
|
||||
{ 0.07295966f, 0.07295966f, 0.00000000f },
|
||||
{ 1.00000000f, -0.85408069f, 0.00000000f }
|
||||
};
|
||||
|
||||
// scipy.signal.butter(1, 300 / 4000.0, 'lowpass', analog=False)
|
||||
// NOTE: Technically, order-1 filter, b[2] = a[2] = 0.
|
||||
constexpr iir_biquad_config_t audio_8k_deemph_300_6_config {
|
||||
{ 0.10583178f, 0.10583178f, 0.00000000f },
|
||||
{ 1.00000000f, -0.78833643f, 0.00000000f }
|
||||
};
|
||||
|
||||
// 75us RC time constant, used in broadcast FM in Americas, South Korea
|
||||
// scipy.signal.butter(1, 2122 / 24000.0, 'lowpass', analog=False)
|
||||
// NOTE: Technically, order-1 filter, b[2] = a[2] = 0.
|
||||
constexpr iir_biquad_config_t audio_48k_deemph_2122_6_config {
|
||||
{ 0.12264116f, 0.12264116f, 0.00000000f },
|
||||
{ 1.00000000f, -0.75471767f, 0.00000000f }
|
||||
};
|
||||
|
||||
#endif/*__DSP_IIR_CONFIG_H__*/
|
@ -394,20 +394,29 @@ void ILI9341::draw_pixels(
|
||||
io.lcd_write_pixels(colors, count);
|
||||
}
|
||||
|
||||
void ILI9341::draw_bitmap(
|
||||
const ui::Point p,
|
||||
const ui::Size size,
|
||||
const uint8_t* const pixels,
|
||||
const ui::Color foreground,
|
||||
const ui::Color background
|
||||
) {
|
||||
lcd_start_ram_write(p, size);
|
||||
|
||||
const size_t count = size.w * size.h;
|
||||
for(size_t i=0; i<count; i++) {
|
||||
const auto pixel = pixels[i >> 3] & (1U << (i & 0x7));
|
||||
io.lcd_write_pixel(pixel ? foreground : background);
|
||||
}
|
||||
}
|
||||
|
||||
void ILI9341::draw_glyph(
|
||||
const ui::Point p,
|
||||
const ui::Glyph& glyph,
|
||||
const ui::Color foreground,
|
||||
const ui::Color background
|
||||
) {
|
||||
lcd_start_ram_write(p, glyph.size());
|
||||
|
||||
const size_t count = glyph.w() * glyph.h();
|
||||
const auto pixels = glyph.pixels();
|
||||
for(size_t i=0; i<count; i++) {
|
||||
const auto pixel = pixels[i >> 3] & (1U << (i & 0x7));
|
||||
io.lcd_write_pixel(pixel ? foreground : background);
|
||||
}
|
||||
draw_bitmap(p, glyph.size(), glyph.pixels(), foreground, background);
|
||||
}
|
||||
|
||||
void ILI9341::scroll_set_area(
|
||||
|
@ -69,6 +69,14 @@ public:
|
||||
draw_pixels(r, colors.data(), colors.size());
|
||||
}
|
||||
|
||||
void draw_bitmap(
|
||||
const ui::Point p,
|
||||
const ui::Size size,
|
||||
const uint8_t* const data,
|
||||
const ui::Color foreground,
|
||||
const ui::Color background
|
||||
);
|
||||
|
||||
void draw_glyph(
|
||||
const ui::Point p,
|
||||
const ui::Glyph& glyph,
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "baseband_packet.hpp"
|
||||
#include "ert_packet.hpp"
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
#include "fifo.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
@ -38,7 +39,7 @@
|
||||
|
||||
class Message {
|
||||
public:
|
||||
static constexpr size_t MAX_SIZE = 276;
|
||||
static constexpr size_t MAX_SIZE = 512;
|
||||
|
||||
enum class ID : uint32_t {
|
||||
/* Assign consecutive IDs. IDs are used to index array. */
|
||||
@ -326,19 +327,28 @@ public:
|
||||
const fir_taps_real<24> decim_0_filter,
|
||||
const fir_taps_real<32> decim_1_filter,
|
||||
const fir_taps_real<32> channel_filter,
|
||||
const size_t deviation
|
||||
const size_t channel_decimation,
|
||||
const size_t deviation,
|
||||
const iir_biquad_config_t audio_hpf_config,
|
||||
const iir_biquad_config_t audio_deemph_config
|
||||
) : Message { ID::NBFMConfigure },
|
||||
decim_0_filter(decim_0_filter),
|
||||
decim_1_filter(decim_1_filter),
|
||||
channel_filter(channel_filter),
|
||||
deviation { deviation }
|
||||
channel_decimation { channel_decimation },
|
||||
deviation { deviation },
|
||||
audio_hpf_config(audio_hpf_config),
|
||||
audio_deemph_config(audio_deemph_config)
|
||||
{
|
||||
}
|
||||
|
||||
const fir_taps_real<24> decim_0_filter;
|
||||
const fir_taps_real<32> decim_1_filter;
|
||||
const fir_taps_real<32> channel_filter;
|
||||
const size_t channel_decimation;
|
||||
const size_t deviation;
|
||||
const iir_biquad_config_t audio_hpf_config;
|
||||
const iir_biquad_config_t audio_deemph_config;
|
||||
};
|
||||
|
||||
class WFMConfigureMessage : public Message {
|
||||
@ -347,12 +357,16 @@ public:
|
||||
const fir_taps_real<24> decim_0_filter,
|
||||
const fir_taps_real<16> decim_1_filter,
|
||||
const fir_taps_real<64> audio_filter,
|
||||
const size_t deviation
|
||||
const size_t deviation,
|
||||
const iir_biquad_config_t audio_hpf_config,
|
||||
const iir_biquad_config_t audio_deemph_config
|
||||
) : Message { ID::WFMConfigure },
|
||||
decim_0_filter(decim_0_filter),
|
||||
decim_1_filter(decim_1_filter),
|
||||
audio_filter(audio_filter),
|
||||
deviation { deviation }
|
||||
deviation { deviation },
|
||||
audio_hpf_config(audio_hpf_config),
|
||||
audio_deemph_config(audio_deemph_config)
|
||||
{
|
||||
}
|
||||
|
||||
@ -360,24 +374,40 @@ public:
|
||||
const fir_taps_real<16> decim_1_filter;
|
||||
const fir_taps_real<64> audio_filter;
|
||||
const size_t deviation;
|
||||
const iir_biquad_config_t audio_hpf_config;
|
||||
const iir_biquad_config_t audio_deemph_config;
|
||||
};
|
||||
|
||||
class AMConfigureMessage : public Message {
|
||||
public:
|
||||
enum class Modulation : int32_t {
|
||||
DSB = 0,
|
||||
SSB = 1,
|
||||
};
|
||||
|
||||
constexpr AMConfigureMessage(
|
||||
const fir_taps_real<24> decim_0_filter,
|
||||
const fir_taps_real<32> decim_1_filter,
|
||||
const fir_taps_real<32> channel_filter
|
||||
const fir_taps_real<32> decim_2_filter,
|
||||
const fir_taps_complex<64> channel_filter,
|
||||
const Modulation modulation,
|
||||
const iir_biquad_config_t audio_hpf_config
|
||||
) : Message { ID::AMConfigure },
|
||||
decim_0_filter(decim_0_filter),
|
||||
decim_1_filter(decim_1_filter),
|
||||
channel_filter(channel_filter)
|
||||
decim_2_filter(decim_2_filter),
|
||||
channel_filter(channel_filter),
|
||||
modulation { modulation },
|
||||
audio_hpf_config(audio_hpf_config)
|
||||
{
|
||||
}
|
||||
|
||||
const fir_taps_real<24> decim_0_filter;
|
||||
const fir_taps_real<32> decim_1_filter;
|
||||
const fir_taps_real<32> channel_filter;
|
||||
const fir_taps_real<32> decim_2_filter;
|
||||
const fir_taps_complex<64> channel_filter;
|
||||
const Modulation modulation;
|
||||
const iir_biquad_config_t audio_hpf_config;
|
||||
};
|
||||
|
||||
class TXDoneMessage : public Message {
|
||||
|
@ -273,6 +273,11 @@ struct Rect {
|
||||
}
|
||||
};
|
||||
|
||||
struct Bitmap {
|
||||
const Size size;
|
||||
const uint8_t* const data;
|
||||
};
|
||||
|
||||
enum class KeyEvent {
|
||||
/* Ordinals map to bit positions reported by CPLD */
|
||||
Right = 0,
|
||||
|
@ -54,6 +54,10 @@ int Painter::draw_string(Point p, const Style& style, const std::string text) {
|
||||
return width;
|
||||
}
|
||||
|
||||
void Painter::draw_bitmap(const Point p, const Bitmap& bitmap, const Color foreground, const Color background) {
|
||||
display.draw_bitmap(p, bitmap.size, bitmap.data, foreground, background);
|
||||
}
|
||||
|
||||
void Painter::draw_hline(Point p, int width, const Color c) {
|
||||
display.fill_rectangle({ p, { width, 1 } }, c);
|
||||
}
|
||||
|
@ -50,6 +50,8 @@ public:
|
||||
|
||||
int draw_string(Point p, const Style& style, const std::string text);
|
||||
|
||||
void draw_bitmap(const Point p, const Bitmap& bitmap, const Color background, const Color foreground);
|
||||
|
||||
void draw_rectangle(const Rect r, const Color c);
|
||||
void fill_rectangle(const Rect r, const Color c);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user