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:
furrtek 2016-02-04 11:35:55 +01:00
commit 8009a9b543
45 changed files with 1148 additions and 877 deletions

View file

@ -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 */

View file

@ -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 */

View file

@ -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() {

View file

@ -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();

View file

@ -61,9 +61,9 @@ ClockManager clock_manager {
i2c0, clock_generator
};
ReceiverModel receiver_model {
clock_manager
};
ReceiverModel receiver_model;
TemperatureLogger temperature_logger;
TemperatureLogger temperature_logger;

View file

@ -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;

View file

@ -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();
}

View file

@ -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();
};

View file

@ -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"

View file

@ -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);
};

View file

@ -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 {

View file

@ -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 */

View file

@ -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__*/

View file

@ -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 */

View file

@ -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);

View file

@ -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();
}

View file

@ -84,7 +84,7 @@ public:
private:
WaterfallView waterfall_view;
FrequencyScale frequency_scale;
ChannelSpectrumFIFO* fifo;
ChannelSpectrumFIFO* fifo { nullptr };
void on_channel_spectrum(const ChannelSpectrum& spectrum);
};