Compare commits

...

9 Commits

Author SHA1 Message Date
Mark Thompson fa06df1400 Persistent audio mute support revisions (#1169)
* Don't disable DAC when other audio output is using it
* Persistent audio mute revisions
* Moved persistent audio mute code to audio.cpp
* Make "Disable AK speaker amp" take effect immediately
2023-06-19 21:06:07 +02:00
Kyle Reed f83027d451 Re-add MicTX workaround (#1170) 2023-06-19 21:05:08 +02:00
Kyle Reed 7ebf3d3cdd Fix hang in Audio when switching from Capture app, & bug fixes. (#1167)
* Fix hang in Audio when switching from Capture app
* Bug fixes
2023-06-19 07:48:29 +02:00
Kyle Reed 5daa0dfbb1 Consolidate frequency field on_edit (#1166) 2023-06-18 21:11:04 +02:00
Mark Thompson 7b669d7001 Proposed default focus changes (#1165)
* Changed default focus of pocsag, glass, scanner, sonde
2023-06-18 08:14:29 +02:00
Mark Thompson ba2407d691 Persistent audio mute support (#1161)
* Persistent speaker mute support #1100
* Moving persistent mute support from WM8731/AK4951 codec files to audio.cpp module for simplification
2023-06-17 23:15:21 +02:00
Kyle Reed 5743d3a3b9 Playlist cleanup (#1163)
* Functional rewrite of playlist
2023-06-17 22:01:46 +02:00
Kyle Reed 7f1c0f6f7e Explicitly set tx_model to make MicTX happy (#1162)
Co-authored-by: kallanreed <kallanreed@noreply.github.com>
2023-06-17 19:15:46 +02:00
Kyle Reed 650d299c99 Don't update radio directly when saving/restoring tx/rx model settings. (#1160)
* Set rf direction in RadioState

* centralize cpld load for TX->RX

* Remove calls to load_sram from all apps

* re-add CPLD hack, don't set radio values directly.

---------

Co-authored-by: kallanreed <kallanreed@noreply.github.com>
2023-06-17 07:54:52 -07:00
85 changed files with 872 additions and 955 deletions
+3 -5
View File
@@ -94,8 +94,6 @@ constexpr std::string_view volume = "volume="sv;
// TODO: Maybe just use a dictionary which would allow for custom settings.
// TODO: Create a control value binding which will allow controls to
// be declaratively bound to a setting and persistence will be magic.
// TODO: radio settings should be pushed and popped to prevent cross-app
// radio bugs caused by sharing a global model.
ResultCode load_settings(const std::string& app_name, AppSettings& settings) {
if (!portapack::persistent_memory::load_app_settings())
@@ -178,17 +176,17 @@ void copy_to_radio_model(const AppSettings& settings) {
if (flags_enabled(settings.mode, Mode::TX)) {
if (!flags_enabled(settings.options, Options::UseGlobalTargetFrequency))
transmitter_model.set_target_frequency(settings.tx_frequency);
persistent_memory::set_target_frequency(settings.tx_frequency);
transmitter_model.configure_from_app_settings(settings);
}
if (flags_enabled(settings.mode, Mode::RX)) {
if (!flags_enabled(settings.options, Options::UseGlobalTargetFrequency))
transmitter_model.set_target_frequency(settings.rx_frequency);
persistent_memory::set_target_frequency(settings.rx_frequency);
receiver_model.configure_from_app_settings(settings);
receiver_model.set_configuration_without_init(
receiver_model.set_configuration_without_update(
static_cast<ReceiverModel::Mode>(settings.modulation),
settings.step,
settings.am_config_index,
+2 -14
View File
@@ -55,7 +55,8 @@ void ACARSLogger::log_raw_data(const acars::Packet& packet, const uint32_t frequ
namespace ui {
ACARSAppView::ACARSAppView(NavigationView& nav) {
ACARSAppView::ACARSAppView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_acars);
add_children({&rssi,
@@ -69,19 +70,6 @@ ACARSAppView::ACARSAppView(NavigationView& nav) {
receiver_model.enable();
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
receiver_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
logging = v;
+6 -6
View File
@@ -27,6 +27,7 @@
#include "radio_state.hpp"
#include "ui_widget.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_rssi.hpp"
#include "log_file.hpp"
@@ -57,6 +58,7 @@ class ACARSAppView : public View {
std::string title() const override { return "ACARS (WIP)"; };
private:
NavigationView& nav_;
RxRadioState radio_state_{
1750000 /* bandwidth */,
2457600 /* sampling rate */
@@ -74,15 +76,13 @@ class ACARSAppView : public View {
VGAGainField field_vga{
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
};
{21 * 8, 0, 6 * 8, 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
};
{21 * 8, 5, 6 * 8, 4}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{0 * 8, 0 * 8},
};
nav_};
Checkbox check_log{
{22 * 8, 21},
3,
+42 -70
View File
@@ -22,21 +22,19 @@
#include "analog_audio_app.hpp"
#include "audio.hpp"
#include "baseband_api.hpp"
#include "file.hpp"
#include "freqman.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
using namespace tonekey;
#include "audio.hpp"
#include "file.hpp"
#include "string_format.hpp"
#include "utility.hpp"
#include "string_format.hpp"
#include "debug.hpp"
#include "freqman.hpp"
using namespace portapack;
using namespace tonekey;
namespace ui {
@@ -45,8 +43,8 @@ namespace ui {
static const Style& style_options_group = Styles::bg_blue;
AMOptionsView::AMOptionsView(
const Rect parent_rect,
const Style* const style)
Rect parent_rect,
const Style* style)
: View{parent_rect} {
set_style(style);
@@ -65,8 +63,8 @@ AMOptionsView::AMOptionsView(
/* NBFMOptionsView *******************************************************/
NBFMOptionsView::NBFMOptionsView(
const Rect parent_rect,
const Style* const style)
Rect parent_rect,
const Style* style)
: View{parent_rect} {
set_style(style);
@@ -90,8 +88,8 @@ NBFMOptionsView::NBFMOptionsView(
/* WFMOptionsView *******************************************************/
WFMOptionsView::WFMOptionsView(
const Rect parent_rect,
const Style* const style)
Rect parent_rect,
const Style* style)
: View{parent_rect} {
set_style(style);
@@ -111,8 +109,8 @@ WFMOptionsView::WFMOptionsView(
SPECOptionsView::SPECOptionsView(
AnalogAudioView* view,
const Rect parent_rect,
const Style* const style)
Rect parent_rect,
const Style* style)
: View{parent_rect} {
set_style(style);
@@ -137,6 +135,10 @@ SPECOptionsView::SPECOptionsView(
AnalogAudioView::AnalogAudioView(
NavigationView& nav)
: nav_(nav) {
// A baseband image _must_ be running before
// interacting with the waterfall view.
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
add_children({&rssi,
&channel,
&audio,
@@ -152,19 +154,6 @@ AnalogAudioView::AnalogAudioView(
// Filename Datetime and Frequency
record_view.set_filename_date_frequency(true);
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.on_change = [this](rf::Frequency f) {
receiver_model.set_target_frequency(f);
};
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
field_frequency.on_show_options = [this]() {
this->on_show_options_frequency();
};
@@ -177,9 +166,12 @@ AnalogAudioView::AnalogAudioView(
this->on_show_options_rf_gain();
};
const auto modulation = receiver_model.modulation();
options_modulation.set_by_value(toUType(modulation));
auto modulation = receiver_model.modulation();
// This app doesn't handle "Capture" mode.
if (modulation > ReceiverModel::Mode::SpectrumAnalysis)
modulation = ReceiverModel::Mode::SpectrumAnalysis;
options_modulation.set_by_value(toUType(modulation));
options_modulation.on_change = [this](size_t, OptionsField::value_t v) {
this->on_modulation_changed(static_cast<ReceiverModel::Mode>(v));
};
@@ -197,8 +189,9 @@ AnalogAudioView::AnalogAudioView(
audio::output::start();
update_modulation(static_cast<ReceiverModel::Mode>(modulation));
on_modulation_changed(static_cast<ReceiverModel::Mode>(modulation));
// This call starts the correct baseband image to run
// and sets the radio up as necessary for the given modulation.
on_modulation_changed(modulation);
}
size_t AnalogAudioView::get_spec_bw_index() {
@@ -225,40 +218,15 @@ void AnalogAudioView::set_spec_trigger(uint16_t trigger) {
}
AnalogAudioView::~AnalogAudioView() {
// // save app settings
// app_settings.rx_frequency = field_frequency.value();
// app_settings.lna = receiver_model.lna();
// app_settings.vga = receiver_model.vga();
// app_settings.rx_amp = receiver_model.rf_amp();
// app_settings.step = receiver_model.frequency_step();
// app_settings.modulation = (uint8_t)receiver_model.modulation();
// app_settings.am_config_index = receiver_model.am_configuration();
// app_settings.nbfm_config_index = receiver_model.nbfm_configuration();
// app_settings.wfm_config_index = receiver_model.wfm_configuration();
// app_settings.squelch = receiver_model.squelch_level();
// settings.save("rx_audio", &app_settings);
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
// both?
audio::output::stop();
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps if the last modulation was SPEC
receiver_model.disable();
baseband::shutdown();
}
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) {
void AnalogAudioView::set_parent_rect(Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
const ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
waterfall.set_parent_rect(waterfall_rect);
}
@@ -270,13 +238,15 @@ void AnalogAudioView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) {
receiver_model.set_baseband_bandwidth(bandwidth_hz);
}
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();
void AnalogAudioView::on_modulation_changed(ReceiverModel::Mode modulation) {
// This app doesn't know what to do with "Capture" mode.
if (modulation > ReceiverModel::Mode::SpectrumAnalysis)
modulation = ReceiverModel::Mode::SpectrumAnalysis;
baseband::spectrum_streaming_stop();
update_modulation(modulation);
on_show_options_modulation();
waterfall.on_show();
baseband::spectrum_streaming_start();
}
void AnalogAudioView::remove_options_widget() {
@@ -328,7 +298,7 @@ void AnalogAudioView::on_show_options_rf_gain() {
void AnalogAudioView::on_show_options_modulation() {
std::unique_ptr<Widget> widget;
const auto modulation = static_cast<ReceiverModel::Mode>(receiver_model.modulation());
const auto modulation = receiver_model.modulation();
switch (modulation) {
case ReceiverModel::Mode::AMAudio:
widget = std::make_unique<AMOptionsView>(options_view_rect, &style_options_group);
@@ -355,6 +325,7 @@ void AnalogAudioView::on_show_options_modulation() {
break;
default:
chDbgPanic("Unhandled Mode");
break;
}
@@ -371,7 +342,7 @@ void AnalogAudioView::on_reference_ppm_correction_changed(int32_t v) {
persistent_memory::set_correction_ppb(v * 1000);
}
void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
void AnalogAudioView::update_modulation(ReceiverModel::Mode modulation) {
audio::output::mute();
record_view.stop();
@@ -392,7 +363,8 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
image_tag = portapack::spi_flash::image_tag_wideband_spectrum;
break;
default:
return;
chDbgPanic("Unhandled Mode");
break;
}
baseband::run_image(image_tag);
@@ -431,7 +403,7 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
}
}
void AnalogAudioView::handle_coded_squelch(const uint32_t value) {
void AnalogAudioView::handle_coded_squelch(uint32_t value) {
float diff, min_diff = value;
size_t min_idx{0};
size_t c;
+16 -33
View File
@@ -26,6 +26,7 @@
#include "receiver_model.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_spectrum.hpp"
#include "ui_record_view.hpp"
#include "ui_styles.hpp"
@@ -37,7 +38,7 @@ namespace ui {
class AMOptionsView : public View {
public:
AMOptionsView(const Rect parent_rect, const Style* const style);
AMOptionsView(Rect parent_rect, const Style* style);
private:
Text label_config{
@@ -55,7 +56,7 @@ class AMOptionsView : public View {
class NBFMOptionsView : public View {
public:
NBFMOptionsView(const Rect parent_rect, const Style* const style);
NBFMOptionsView(Rect parent_rect, const Style* style);
private:
Text label_config{
@@ -83,7 +84,7 @@ class NBFMOptionsView : public View {
class WFMOptionsView : public View {
public:
WFMOptionsView(const Rect parent_rect, const Style* const style);
WFMOptionsView(Rect parent_rect, const Style* style);
private:
Text label_config{
@@ -102,7 +103,7 @@ class AnalogAudioView;
class SPECOptionsView : public View {
public:
SPECOptionsView(AnalogAudioView* view, const Rect parent_rect, const Style* const style);
SPECOptionsView(AnalogAudioView* view, Rect parent_rect, const Style* style);
private:
Text label_config{
@@ -139,10 +140,7 @@ class AnalogAudioView : public View {
AnalogAudioView(NavigationView& nav);
~AnalogAudioView();
void on_hide() override;
void set_parent_rect(const Rect new_parent_rect) override;
void set_parent_rect(Rect new_parent_rect) override;
void focus() override;
std::string title() const override { return "Audio RX"; };
@@ -156,6 +154,7 @@ class AnalogAudioView : public View {
private:
static constexpr ui::Dim header_height = 3 * 16;
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_audio", app_settings::Mode::RX,
@@ -168,24 +167,18 @@ class AnalogAudioView : public View {
uint32_t spec_bw = 20000000;
uint16_t spec_trigger = 63;
NavigationView& nav_;
// bool exit_on_squelch { false };
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
};
{21 * 8, 0, 6 * 8, 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
};
{21 * 8, 5, 6 * 8, 4}};
Audio audio{
{21 * 8, 10, 6 * 8, 4},
};
{21 * 8, 10, 6 * 8, 4}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{5 * 8, 0 * 16},
};
nav_};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
@@ -223,33 +216,23 @@ class AnalogAudioView : public View {
spectrum::WaterfallWidget waterfall{true};
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
void on_modulation_changed(const ReceiverModel::Mode modulation);
void on_modulation_changed(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_edit_frequency();
void remove_options_widget();
void set_options_widget(std::unique_ptr<Widget> new_widget);
void update_modulation(const ReceiverModel::Mode modulation);
void update_modulation(ReceiverModel::Mode modulation);
// void squelched();
void handle_coded_squelch(const uint32_t value);
/*MessageHandlerRegistration message_handler_squelch_signal {
Message::ID::RequestSignal,
[this](const Message* const p) {
(void)p;
this->squelched();
}
};*/
void handle_coded_squelch(uint32_t value);
MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch,
[this](const Message* const p) {
[this](const Message* p) {
const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p);
this->handle_coded_squelch(message.value);
}};
@@ -56,22 +56,6 @@ AnalogTvView::AnalogTvView(
&field_volume,
&tv});
// Set on_change before initialising the field
field_frequency.on_change = [this](rf::Frequency f) {
this->on_target_frequency_changed(f);
};
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->on_target_frequency_changed(f);
this->field_frequency.set_value(f);
};
};
field_frequency.on_show_options = [this]() {
this->on_show_options_frequency();
};
@@ -125,10 +109,6 @@ void AnalogTvView::focus() {
field_frequency.focus();
}
void AnalogTvView::on_target_frequency_changed(rf::Frequency f) {
receiver_model.set_target_frequency(f);
}
void AnalogTvView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) {
receiver_model.set_baseband_bandwidth(bandwidth_hz);
}
+7 -12
View File
@@ -27,6 +27,7 @@
#include "receiver_model.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_tv.hpp"
#include "ui_record_view.hpp"
#include "ui_styles.hpp"
@@ -53,6 +54,7 @@ class AnalogTvView : public View {
private:
static constexpr ui::Dim header_height = 3 * 16;
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_tv", app_settings::Mode::RX};
@@ -60,23 +62,18 @@ class AnalogTvView : public View {
const Rect options_view_rect{0 * 8, 1 * 16, 30 * 8, 1 * 16};
const Rect nbfm_view_rect{0 * 8, 1 * 16, 18 * 8, 1 * 16};
NavigationView& nav_;
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
};
{21 * 8, 0, 6 * 8, 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
};
{21 * 8, 5, 6 * 8, 4}};
Audio audio{
{21 * 8, 10, 6 * 8, 4},
};
{21 * 8, 10, 6 * 8, 4}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{5 * 8, 0 * 16},
};
nav_};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
@@ -100,7 +97,6 @@ class AnalogTvView : public View {
tv::TVWidget tv{};
void on_target_frequency_changed(rf::Frequency f);
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
void on_modulation_changed(const ReceiverModel::Mode modulation);
void on_show_options_frequency();
@@ -108,7 +104,6 @@ class AnalogTvView : public View {
void on_show_options_modulation();
void on_frequency_step_changed(rf::Frequency f);
void on_reference_ppm_correction_changed(int32_t v);
void on_edit_frequency();
void remove_options_widget();
void set_options_widget(std::unique_ptr<Widget> new_widget);
+19 -37
View File
@@ -21,15 +21,15 @@
*/
#include "capture_app.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
using namespace portapack;
namespace ui {
CaptureAppView::CaptureAppView(NavigationView& nav) {
CaptureAppView::CaptureAppView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_capture);
add_children({
@@ -46,20 +46,6 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
&waterfall,
});
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
this->on_target_frequency_changed(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->on_target_frequency_changed(f);
this->field_frequency.set_value(f);
};
};
field_frequency_step.set_by_value(receiver_model.frequency_step());
field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) {
receiver_model.set_frequency_step(v);
@@ -68,15 +54,12 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
option_bandwidth.on_change = [this](size_t, uint32_t base_rate) {
sampling_rate = 8 * base_rate; // Decimation by 8 done on baseband side
/* base_rate is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
/* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz , when selected 1 Mhz BW ...*/
/* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */
/* base_rate is used for FFT calculation and display LCD, and also in recording writing SD Card rate. */
/* ex. sampling_rate values, 4Mhz, when recording 500 kHz (BW) and fs 8 Mhz, when selected 1 Mhz BW ... */
/* ex. recording 500kHz BW to .C16 file, base_rate clock 500kHz x2(I,Q) x 2 bytes (int signed) =2MB/sec rate SD Card */
waterfall.on_hide();
record_view.set_sampling_rate(sampling_rate);
receiver_model.set_sampling_rate(sampling_rate);
/* Set up proper anti aliasing BPF bandwith in MAX2837 before ADC sampling according to the new added BW Options . */
/* Set up proper anti aliasing BPF bandwith in MAX2837 before ADC sampling according to the new added BW Options . */
switch (sampling_rate) { // we use the var fs (sampling_rate) , to set up BPF aprox < fs_max/2 by Nyquist theorem.
case 0 ... 2000000: // BW Captured range (0 <= 250kHz max ) fs = 8 x 250 kHz
@@ -93,30 +76,33 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
break;
case 14000000: // BW capture 1,75Mhz , fs = 8 x 1,75Mhz = 14Mhz
// good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture
// good BPF, good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture
anti_alias_baseband_bandwidth_filter = 5000000;
break;
case 16000000: // BW capture 2Mhz , fs = 8 x 2Mhz = 16Mhz
// good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture
// good BPF, good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture
anti_alias_baseband_bandwidth_filter = 6000000;
break;
case 20000000: // BW capture 2,5Mhz , fs= 8 x 2,5 Mhz = 20Mhz
// good BPF ,good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture
// good BPF, good matching, but LCD making flicker , refresh rate should be < 20 Hz , but reasonable picture
anti_alias_baseband_bandwidth_filter = 7000000;
break;
default: // BW capture 2,75Mhz, fs = 8 x 2,75Mhz= 22Mhz max ADC sampling) and others.
// We tested also 9Mhz FPB stightly too much noise floor , better 8Mhz
default: // BW capture 2,75Mhz, fs = 8 x 2,75Mhz= 22Mhz max ADC sampling) and others.
// We tested also 9Mhz FPB stightly too much noise floor, better 8Mhz
anti_alias_baseband_bandwidth_filter = 8000000;
}
record_view.set_sampling_rate(sampling_rate);
receiver_model.set_sampling_rate(sampling_rate);
receiver_model.set_baseband_bandwidth(anti_alias_baseband_bandwidth_filter);
waterfall.on_show();
};
option_bandwidth.set_selected_index(7); // 500k, Preselected starting default option 500kHz
option_bandwidth.set_selected_index(7); // Preselected default option 500kHz.
receiver_model.set_modulation(ReceiverModel::Mode::Capture);
receiver_model.enable();
@@ -127,13 +113,13 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
}
CaptureAppView::~CaptureAppView() {
// Most other apps can't handle "Capture" mode, set to something standard.
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
receiver_model.disable();
baseband::shutdown();
}
void CaptureAppView::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();
}
@@ -141,7 +127,7 @@ void CaptureAppView::on_hide() {
void CaptureAppView::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(), new_parent_rect.height() - header_height};
ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
waterfall.set_parent_rect(waterfall_rect);
}
@@ -149,8 +135,4 @@ void CaptureAppView::focus() {
record_view.focus();
}
void CaptureAppView::on_target_frequency_changed(rf::Frequency f) {
receiver_model.set_target_frequency(f);
}
} /* namespace ui */
+29 -30
View File
@@ -26,26 +26,28 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_record_view.hpp"
#include "ui_spectrum.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#define BW_OPTIONS \
{" 8k5", 8500}, \
{" 11k", 11000}, \
{" 16k", 16000}, \
{" 25k", 25000}, \
{" 50k", 50000}, \
{" 100k", 100000}, \
{" 250k", 250000}, \
{" 500k", 500000}, /* Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.*/ \
{" 600k", 600000}, /* That extended option is still possible to record with FW version Mayhem v1.41 (< 2,5MB/sec) */ \
{" 750k", 750000}, /* From that BW onwards, the LCD is ok, but the recorded file is auto decimated,(not real file size) */ \
{"1100k", 1100000}, \
{"1750k", 1750000}, \
{"2000k", 2000000}, \
{"2500k", 2500000}, \
{"2750k", 2750000}, // That is our max Capture option , to keep using later / 8 decimation (22Mhz sampling ADC)
#define BW_OPTIONS \
{" 8k5", 8500}, \
{" 11k", 11000}, \
{" 16k", 16000}, \
{" 25k", 25000}, \
{" 50k", 50000}, \
{" 100k", 100000}, \
{" 250k", 250000}, \
{" 500k", 500000}, /* Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.*/ \
{" 600k", 600000}, /* That extended option is still possible to record with FW version Mayhem v1.41 (< 2,5MB/sec) */ \
{" 750k", 750000}, /* From this BW onwards, the LCD is ok, but the recorded file is decimated, (not real file size) */ \
{"1100k", 1100000}, \
{"1750k", 1750000}, \
{"2000k", 2000000}, \
{"2500k", 2500000}, \
{"2750k", 2750000}, // That is our max Capture option, to keep using later / 8 decimation (22Mhz sampling ADC)
namespace ui {
@@ -55,42 +57,39 @@ class CaptureAppView : public View {
~CaptureAppView();
void on_hide() override;
void set_parent_rect(const Rect new_parent_rect) override;
void focus() override;
void set_parent_rect(const Rect new_parent_rect) override;
std::string title() const override { return "Capture"; };
private:
static constexpr ui::Dim header_height = 3 * 16;
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_capture", app_settings::Mode::RX,
app_settings::Options::UseGlobalTargetFrequency};
uint32_t sampling_rate = 0;
uint32_t anti_alias_baseband_bandwidth_filter = 2500000; // we rename the previous var , and change type static constexpr to normal var.
void on_target_frequency_changed(rf::Frequency f);
uint32_t anti_alias_baseband_bandwidth_filter = 2500000;
Labels labels{
{{0 * 8, 1 * 16}, "Rate:", Color::light_grey()},
};
RSSI rssi{
{24 * 8, 0, 6 * 8, 4},
};
{24 * 8, 0, 6 * 8, 4}};
Channel channel{
{24 * 8, 5, 6 * 8, 4},
};
{24 * 8, 5, 6 * 8, 4}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
};
nav_};
FrequencyStepView field_frequency_step{
{10 * 8, 0 * 16},
};
{10 * 8, 0 * 16}};
RFAmpField field_rf_amp{
{16 * 8, 0 * 16}};
+3 -15
View File
@@ -26,10 +26,10 @@
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "utility.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "cpld_update.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
@@ -81,7 +81,7 @@ void GpsSimAppView::on_file_changed(std::filesystem::path new_file_path) {
text_sample_rate.set(unit_auto_scale(sample_rate, 3, 1) + "Hz");
auto file_size = data_file.size();
auto duration = (file_size * 1000) / (1 * 2 * sample_rate);
auto duration = ms_duration(file_size, sample_rate, 2);
progressbar.set_max(file_size / 1024);
text_filename.set(file_path.filename().string().substr(0, 12));
@@ -187,18 +187,7 @@ GpsSimAppView::GpsSimAppView(
&waterfall,
});
field_frequency.set_value(transmitter_model.target_frequency());
field_frequency.set_step(5000);
field_frequency.on_change = [this](rf::Frequency f) {
transmitter_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->field_frequency.set_value(f);
};
};
button_play.on_select = [this](ImageButton&) {
this->toggle();
@@ -214,8 +203,7 @@ GpsSimAppView::GpsSimAppView(
GpsSimAppView::~GpsSimAppView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
void GpsSimAppView::on_hide() {
+3 -2
View File
@@ -32,6 +32,7 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "replay_thread.hpp"
#include "ui_spectrum.hpp"
#include "ui_transmitter.hpp"
@@ -102,9 +103,9 @@ class GpsSimAppView : public View {
ProgressBar progressbar{
{18 * 8, 1 * 16, 12 * 8, 16}};
FrequencyField field_frequency{
TxFrequencyField field_frequency{
{0 * 8, 2 * 16},
};
nav_};
TransmitterView2 tx_view{
// new handling of NumberField field_rfgain, NumberField field_rfamp
+1 -3
View File
@@ -27,7 +27,6 @@
#include "lge_app.hpp"
#include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "ui_textentry.hpp"
#include "string_format.hpp"
@@ -45,8 +44,7 @@ void LGEView::focus() {
LGEView::~LGEView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
void LGEView::generate_lge_frame(const uint8_t command, const uint16_t address_a, const uint16_t address_b, std::vector<uint8_t>& data) {
+17 -29
View File
@@ -51,9 +51,8 @@ void POCSAGLogger::log_decoded(
namespace ui {
POCSAGAppView::POCSAGAppView(NavigationView& nav) {
uint32_t ignore_address;
POCSAGAppView::POCSAGAppView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_pocsag);
add_children({&rssi,
@@ -70,28 +69,16 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
&console});
if (!settings_.loaded())
receiver_model.set_target_frequency(initial_target_frequency);
field_frequency.set_value(initial_target_frequency);
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
receiver_model.enable();
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.on_change = [this](rf::Frequency f) {
receiver_model.set_target_frequency(f);
};
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
// TODO app setting instead?
// TODO: app setting instead?
uint32_t ignore_address;
ignore_address = persistent_memory::pocsag_ignore_address();
// TODO is this common enough for a helper?
// TODO: is this common enough for a helper?
for (size_t c = 0; c < 7; c++) {
sym_ignore.set_sym(6 - c, ignore_address % 10);
ignore_address /= 10;
@@ -102,13 +89,12 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
logger->append(LOG_ROOT_DIR "/POCSAG.TXT");
audio::output::start();
audio::output::unmute();
baseband::set_pocsag();
}
void POCSAGAppView::focus() {
check_log.focus();
field_frequency.focus();
}
POCSAGAppView::~POCSAGAppView() {
@@ -157,9 +143,10 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
console.write(console_info);
if (logger && logging()) {
logger->log_decoded(message->packet, to_string_dec_uint(pocsag_state.address) +
" F" + to_string_dec_uint(pocsag_state.function) +
" Address only");
logger->log_decoded(
message->packet, to_string_dec_uint(pocsag_state.address) +
" F" + to_string_dec_uint(pocsag_state.function) +
" Address only");
}
last_address = pocsag_state.address;
@@ -176,16 +163,17 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
}
if (logger && logging())
logger->log_decoded(message->packet, to_string_dec_uint(pocsag_state.address) +
" F" + to_string_dec_uint(pocsag_state.function) +
" Alpha: " + pocsag_state.output);
logger->log_decoded(
message->packet, to_string_dec_uint(pocsag_state.address) +
" F" + to_string_dec_uint(pocsag_state.function) +
" Alpha: " + pocsag_state.output);
}
}
// TODO: make setting.
// Log raw data whatever it contains
if (logger && logging())
logger->log_raw_data(message->packet, receiver_model.target_frequency());
/*if (logger && logging())
logger->log_raw_data(message->packet, receiver_model.target_frequency());*/
}
} /* namespace ui */
+7 -8
View File
@@ -24,6 +24,7 @@
#define __POCSAG_APP_H__
#include "ui_widget.hpp"
#include "ui_freq_field.hpp"
#include "ui_receiver.hpp"
#include "ui_rssi.hpp"
@@ -61,6 +62,7 @@ class POCSAGAppView : public View {
bool logging() const { return check_log.value(); };
bool ignore() const { return check_ignore.value(); };
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_pocsag", app_settings::Mode::RX};
@@ -75,18 +77,15 @@ class POCSAGAppView : public View {
VGAGainField field_vga{
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
};
{21 * 8, 0, 6 * 8, 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
};
{21 * 8, 5, 6 * 8, 4}};
Audio audio{
{21 * 8, 10, 6 * 8, 4},
};
{21 * 8, 10, 6 * 8, 4}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{0 * 8, 0 * 8},
};
nav_};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
+4 -21
View File
@@ -26,10 +26,10 @@
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "cpld_update.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
#include "utility.hpp"
using namespace portapack;
@@ -80,7 +80,7 @@ void ReplayAppView::on_file_changed(std::filesystem::path new_file_path) {
text_sample_rate.set(unit_auto_scale(sample_rate, 3, 0) + "Hz");
auto file_size = data_file.size();
auto duration = (file_size * 1000) / (2 * 2 * sample_rate);
auto duration = ms_duration(file_size, sample_rate, 4);
progressbar.set_max(file_size);
text_filename.set(file_path.filename().string().substr(0, 12));
@@ -188,27 +188,12 @@ ReplayAppView::ReplayAppView(
&text_duration,
&progressbar,
&field_frequency,
&tx_view, // now it handles previous rfgain , rfamp.
&tx_view, // now it handles previous rfgain, rfamp.
&check_loop,
&button_play,
&waterfall,
});
field_frequency.set_value(transmitter_model.target_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
transmitter_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
field_frequency.set_step(5000);
button_play.on_select = [this](ImageButton&) {
this->toggle();
};
@@ -226,9 +211,7 @@ ReplayAppView::~ReplayAppView() {
display.fill_rectangle({0, 0, 240, 320}, Color::black()); // Solving sometimes visible bottom waterfall artifacts, clearing all LCD pixels.
chThdSleepMilliseconds(40); // (that happened sometimes if we interrupt the waterfall play at the beggining of the play around 25% and exit )
hackrf::cpld::load_sram_no_verify(); // to leave all RX reception ok, without "ghost interference signal problem" at the exit .
baseband::shutdown(); // better this function at the end, after load_sram(). If not , sometimes produced hang up (now not , it is ok).
baseband::shutdown();
}
void ReplayAppView::on_hide() {
+4 -2
View File
@@ -31,6 +31,7 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "replay_thread.hpp"
#include "ui_spectrum.hpp"
#include "ui_transmitter.hpp"
@@ -98,9 +99,10 @@ class ReplayAppView : public View {
ProgressBar progressbar{
{18 * 8, 1 * 16, 12 * 8, 16}};
FrequencyField field_frequency{
// TODO: Does this need to be a frequency field at all?
TxFrequencyField field_frequency{
{0 * 8, 2 * 16},
};
nav_};
TransmitterView2 tx_view{
// new handling of NumberField field_rfgain, NumberField field_rfamp
+1 -3
View File
@@ -25,7 +25,6 @@
#include "soundboard_app.hpp"
#include "string_format.hpp"
#include "tonesets.hpp"
#include "cpld_update.hpp"
using namespace tonekey;
using namespace portapack;
@@ -275,8 +274,7 @@ SoundBoardView::SoundBoardView(
SoundBoardView::~SoundBoardView() {
stop();
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
} // namespace ui
+1 -3
View File
@@ -26,7 +26,6 @@
#include "manchester.hpp"
#include "string_format.hpp"
#include "portapack.hpp"
#include "cpld_update.hpp"
#include "baseband_api.hpp"
#include <cstring>
@@ -274,8 +273,7 @@ void ADSBTxView::focus() {
ADSBTxView::~ADSBTxView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, withouth ghost signal problem at the exit.
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
void ADSBTxView::generate_frames() {
+4 -17
View File
@@ -43,11 +43,8 @@ void AFSKRxView::focus() {
field_frequency.focus();
}
void AFSKRxView::update_freq(rf::Frequency f) {
receiver_model.set_target_frequency(f);
}
AFSKRxView::AFSKRxView(NavigationView& nav) {
AFSKRxView::AFSKRxView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_afsk_rx);
add_children({&rssi,
@@ -62,8 +59,8 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
&button_modem_setup,
&console});
// Auto-configure modem for LCR RX (will be removed later)
update_freq(467225500); // 462713300
// Auto-configure modem for LCR RX (TODO remove)
field_frequency.set_value(467225500);
auto def_bell202 = &modem_defs[0];
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
serial_format_t serial_format;
@@ -73,17 +70,7 @@ AFSKRxView::AFSKRxView(NavigationView& nav) {
serial_format.bit_order = LSB_FIRST;
persistent_memory::set_serial_format(serial_format);
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.set_step(100);
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
+7 -8
View File
@@ -26,7 +26,8 @@
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_record_view.hpp" // DEBUG
#include "ui_freq_field.hpp"
#include "ui_record_view.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "log_file.hpp"
@@ -58,6 +59,7 @@ class AFSKRxView : public View {
private:
void on_data(uint32_t value, bool is_data);
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_afsk", app_settings::Mode::RX};
@@ -74,18 +76,16 @@ class AFSKRxView : public View {
VGAGainField field_vga{
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
};
{21 * 8, 0, 6 * 8, 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
};
{21 * 8, 5, 6 * 8, 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
};
nav_};
Checkbox check_log{
{0 * 8, 1 * 16},
@@ -104,7 +104,6 @@ class AFSKRxView : public View {
Console console{
{0, 4 * 16, 240, 240}};
void update_freq(rf::Frequency f);
void on_data_afsk(const AFSKDataMessage& message);
std::unique_ptr<AFSKLogger> logger{};
+1 -16
View File
@@ -73,12 +73,8 @@ void APRSRxView::focus() {
options_region.focus();
}
void APRSRxView::update_freq(rf::Frequency f) {
receiver_model.set_target_frequency(f);
}
APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
: View(parent_rect) {
: View(parent_rect), nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_aprs_rx);
add_children({&rssi,
@@ -111,18 +107,7 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
}
};
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.set_step(100);
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
options_region.set_selected_index(0, true);
logger = std::make_unique<APRSLogger>();
+7 -9
View File
@@ -26,7 +26,8 @@
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_record_view.hpp" // DEBUG
#include "ui_freq_field.hpp"
#include "ui_record_view.hpp"
#include "ui_geomap.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
@@ -188,6 +189,7 @@ class APRSRxView : public View {
void on_data(uint32_t value, bool is_data);
bool reset_console = false;
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_aprs", app_settings::Mode::RX};
@@ -202,11 +204,9 @@ class APRSRxView : public View {
VGAGainField field_vga{
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
};
{21 * 8, 0, 6 * 8, 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
};
{21 * 8, 5, 6 * 8, 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
@@ -219,9 +219,9 @@ class APRSRxView : public View {
{"AUS", 2},
{"NZ ", 3}}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{3 * 8, 0 * 16},
};
nav_};
// DEBUG
RecordView record_view{
@@ -235,8 +235,6 @@ class APRSRxView : public View {
Console console{
{0, 2 * 16, 240, 240}};
void update_freq(rf::Frequency f);
std::unique_ptr<APRSLogger> logger{};
};
+1 -3
View File
@@ -26,7 +26,6 @@
#include "aprs.hpp"
#include "string_format.hpp"
#include "portapack.hpp"
#include "cpld_update.hpp"
#include "baseband_api.hpp"
#include "portapack_shared_memory.hpp"
#include "portapack_persistent_memory.hpp"
@@ -45,8 +44,7 @@ void APRSTXView::focus() {
APRSTXView::~APRSTXView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
void APRSTXView::start_tx() {
+1 -3
View File
@@ -24,7 +24,6 @@
#include "string_format.hpp"
#include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
@@ -138,8 +137,7 @@ void BHTView::on_tx_progress(const uint32_t progress, const bool done) {
BHTView::~BHTView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
BHTView::BHTView(NavigationView& nav) {
+4 -17
View File
@@ -40,11 +40,8 @@ void BTLERxView::focus() {
field_frequency.focus();
}
void BTLERxView::update_freq(rf::Frequency f) {
receiver_model.set_target_frequency(f);
}
BTLERxView::BTLERxView(NavigationView& nav) {
BTLERxView::BTLERxView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_btle_rx);
add_children({&rssi,
@@ -56,8 +53,8 @@ BTLERxView::BTLERxView(NavigationView& nav) {
&button_modem_setup,
&console});
// Auto-configure modem for LCR RX (will be removed later)
update_freq(2426000000);
// Auto-configure modem for LCR RX (TODO: remove later)
field_frequency.set_value(2426000000);
auto def_bell202 = &modem_defs[0];
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
serial_format_t serial_format;
@@ -67,17 +64,7 @@ BTLERxView::BTLERxView(NavigationView& nav) {
serial_format.bit_order = LSB_FIRST;
persistent_memory::set_serial_format(serial_format);
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.set_step(100);
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
button_modem_setup.on_select = [&nav](Button&) {
nav.push<ModemSetupView>();
+7 -10
View File
@@ -27,9 +27,10 @@
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui_record_view.hpp" // DEBUG
#include "ui_record_view.hpp"
#include "utility.hpp"
@@ -47,6 +48,7 @@ class BTLERxView : public View {
private:
void on_data(uint32_t value, bool is_data);
NavigationView& nav_;
RxRadioState radio_state_{
4000000 /* bandwidth */,
4000000 /* sampling rate */
@@ -64,15 +66,13 @@ class BTLERxView : public View {
VGAGainField field_vga{
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
};
{21 * 8, 0, 6 * 8, 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
};
{21 * 8, 5, 6 * 8, 4}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
};
nav_};
Button button_modem_setup{
{240 - 12 * 8, 1 * 16, 96, 24},
@@ -81,9 +81,6 @@ class BTLERxView : public View {
Console console{
{0, 4 * 16, 240, 240}};
void update_freq(rf::Frequency f);
// void on_data_afsk(const AFSKDataMessage& message);
MessageHandlerRegistration message_handler_packet{
Message::ID::AFSKData,
[this](Message* const p) {
+1 -3
View File
@@ -23,7 +23,6 @@
#include "ui_coasterp.hpp"
#include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "portapack_persistent_memory.hpp"
#include <cstring>
@@ -39,8 +38,7 @@ void CoasterPagerView::focus() {
CoasterPagerView::~CoasterPagerView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
void CoasterPagerView::generate_frame() {
+1 -3
View File
@@ -24,7 +24,6 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "cpld_update.hpp"
using namespace portapack;
@@ -201,8 +200,7 @@ void EncodersView::focus() {
EncodersView::~EncodersView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // ghost signal c/m to the problem at the exit .
baseband::shutdown(); // better this function after load_sram()
baseband::shutdown();
}
void EncodersView::update_progress() {
+1 -3
View File
@@ -25,7 +25,6 @@
#include "ui_freqman.hpp"
#include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "string_format.hpp"
using namespace portapack;
@@ -180,8 +179,7 @@ void JammerView::focus() {
JammerView::~JammerView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
void JammerView::on_retune(const rf::Frequency freq, const uint32_t range) {
+1 -3
View File
@@ -23,7 +23,6 @@
#include "ui_keyfob.hpp"
#include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "string_format.hpp"
using namespace portapack;
@@ -141,8 +140,7 @@ KeyfobView::~KeyfobView() {
settings.save("tx_keyfob", &app_settings);
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
void KeyfobView::update_progress(const uint32_t progress) {
+1 -3
View File
@@ -26,7 +26,6 @@
#include "lcr.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "cpld_update.hpp"
#include "serializer.hpp"
@@ -40,8 +39,7 @@ void LCRView::focus() {
LCRView::~LCRView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
/*
@@ -30,7 +30,7 @@ using namespace portapack;
namespace ui {
void GlassView::focus() {
button_marker.focus();
range_presets.focus();
}
GlassView::~GlassView() {
@@ -26,6 +26,7 @@
#include "ui.hpp"
#include "portapack.hpp"
#include "app_settings.hpp"
#include "baseband_api.hpp"
#include "radio_state.hpp"
#include "receiver_model.hpp"
@@ -71,6 +72,8 @@ class GlassView : public View {
private:
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_glass", app_settings::Mode::RX};
struct preset_entry {
rf::Frequency min{};
+8 -5
View File
@@ -30,7 +30,6 @@ using wolfson::wm8731::WM8731;
#include "tonesets.hpp"
#include "portapack_hal.hpp"
#include "cpld_update.hpp"
#include "string_format.hpp"
#include "irq_controls.hpp"
@@ -163,7 +162,7 @@ void MicTXView::rxaudio(bool is_on) {
// receiver_model.set_nbfm_configuration(n); is called above , depending user's selection (8k5, 11k, 16k).
}
}
// receiver_model.set_target_frequency(field_frequency.value()); //probably this too can be commented out.
if (bool_same_F_tx_rx_enabled) // when stop TX ,define to which freq RX we return
receiver_model.set_target_frequency(tx_frequency); // Update freq also for RX = TX
else
@@ -172,7 +171,6 @@ void MicTXView::rxaudio(bool is_on) {
receiver_model.set_vga(rx_vga);
receiver_model.set_rf_amp(rx_amp);
receiver_model.enable();
hackrf::cpld::load_sram_no_verify(); // to have a good RX without any ghost inside Mic App
audio::output::start();
} else { // These incredibly convoluted steps are required for the vumeter to reappear when stopping RX.
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio); // This fixes something with AM RX...
@@ -597,6 +595,12 @@ MicTXView::MicTXView(
}
};
// These shouldn't be necessary, but because
// this app uses both transmitter_model and directly
// configures the baseband, these end up being required.
transmitter_model.set_sampling_rate(sampling_rate);
transmitter_model.set_baseband_bandwidth(1750000);
set_tx(false);
audio::set_rate(audio::Rate::Hz_24000);
@@ -609,8 +613,7 @@ MicTXView::~MicTXView() {
transmitter_model.disable();
if (rx_enabled) // Also turn off audio rx if enabled
rxaudio(false);
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
} // namespace ui
+2
View File
@@ -204,6 +204,7 @@ class MicTXView : public View {
{"OFF-08dB", 4}, // WM8731 Mic Boost OFF to avoid ADC sat in high voice ,relative G = -12 dB's (respect ref level)
}};
// TODO: Use TxFrequencyField
FrequencyField field_frequency{
{5 * 8, 3 * 8},
};
@@ -309,6 +310,7 @@ class MicTXView : public View {
' ',
};
// TODO: Use RxFrequencyField
FrequencyField field_rxfrequency{
{10 * 8, (25 * 8) + 2},
};
@@ -38,8 +38,6 @@ class ModemSetupView : public View {
std::string title() const override { return "Modem setup"; };
private:
void update_freq(rf::Frequency f);
Labels labels{
{{2 * 8, 11 * 8}, "Baudrate:", Color::light_grey()},
{{2 * 8, 13 * 8}, "Mark: Hz", Color::light_grey()},
+1 -3
View File
@@ -26,7 +26,6 @@
#include "baseband_api.hpp"
#include "hackrf_gpio.hpp"
#include "portapack_shared_memory.hpp"
#include "cpld_update.hpp"
#include "ui_textentry.hpp"
#include "string_format.hpp"
@@ -99,8 +98,7 @@ void MorseView::focus() {
MorseView::~MorseView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit .
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
void MorseView::paint(Painter&) {
+3 -16
View File
@@ -40,11 +40,8 @@ void NRFRxView::focus() {
field_frequency.focus();
}
void NRFRxView::update_freq(rf::Frequency f) {
receiver_model.set_target_frequency(f);
}
NRFRxView::NRFRxView(NavigationView& nav) {
NRFRxView::NRFRxView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_nrf_rx);
add_children({&rssi,
@@ -57,7 +54,7 @@ NRFRxView::NRFRxView(NavigationView& nav) {
&console});
// Auto-configure modem for LCR RX (will be removed later)
update_freq(2480000000);
field_frequency.set_value(2480000000);
auto def_bell202 = &modem_defs[0];
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
serial_format_t serial_format;
@@ -67,17 +64,7 @@ NRFRxView::NRFRxView(NavigationView& nav) {
serial_format.bit_order = LSB_FIRST;
persistent_memory::set_serial_format(serial_format);
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.set_step(100);
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
button_modem_setup.on_select = [&nav](Button&) {
nav.push<ModemSetupView>();
+6 -9
View File
@@ -27,6 +27,7 @@
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "ui_record_view.hpp" // DEBUG
@@ -47,6 +48,7 @@ class NRFRxView : public View {
private:
void on_data(uint32_t value, bool is_data);
NavigationView& nav_;
RxRadioState radio_state_{
4000000 /* bandwidth */,
4000000 /* sampling rate */
@@ -64,15 +66,13 @@ class NRFRxView : public View {
VGAGainField field_vga{
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
};
{21 * 8, 0, 6 * 8, 4}};
Channel channel{
{21 * 8, 5, 6 * 8, 4},
};
{21 * 8, 5, 6 * 8, 4}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
};
nav_};
Button button_modem_setup{
{240 - 12 * 8, 1 * 16, 96, 24},
@@ -81,9 +81,6 @@ class NRFRxView : public View {
Console console{
{0, 4 * 16, 240, 240}};
void update_freq(rf::Frequency f);
// void on_data_afsk(const AFSKDataMessage& message);
MessageHandlerRegistration message_handler_packet{
Message::ID::AFSKData,
[this](Message* const p) {
+190 -208
View File
@@ -2,6 +2,7 @@
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyleft () 2022 NotPike
* Copyright (C) 2023 Kyle Reed, zxkmm
*
* This file is part of PortaPack.
*
@@ -25,9 +26,9 @@
#include "convert.hpp"
#include "file_reader.hpp"
#include "string_format.hpp"
#include "ui_fileman.hpp"
#include "io_file.hpp"
#include "utility.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
@@ -36,219 +37,215 @@
#include <fstream>
using namespace portapack;
namespace fs = std::filesystem;
/* TODO: wouldn't it be easier if the playlist were just a list of C16 files
* (and maybe delays) and then read the data file next to the C16 file? */
namespace ui {
void PlaylistView::set_ready() {
ready_signal = true;
}
void PlaylistView::load_file(std::filesystem::path playlist_path) {
void PlaylistView::load_file(const fs::path& playlist_path) {
File playlist_file;
auto error = playlist_file.open(playlist_path.string());
if (!error) {
auto reader = FileLineReader(playlist_file);
for (const auto& line : reader) {
if (line.length() == 0 || line[0] == '#')
continue; // Empty or comment line.
if (error)
return;
auto cols = split_string(line, ',');
if (cols.size() < 3)
continue; // Line doesn't have enough columns.
auto reader = FileLineReader(playlist_file);
for (const auto& line : reader) {
if (line.length() == 0 || line[0] == '#')
continue; // Empty or comment line.
playlist_entry entry{};
auto cols = split_string(line, ',');
if (cols.size() < 3)
continue; // Line doesn't have enough columns.
parse_int(cols[0], entry.replay_frequency);
parse_int(cols[2], entry.sample_rate);
if (entry.replay_frequency == 0 || entry.sample_rate == 0)
continue; // Invalid freq or rate.
playlist_entry entry{};
entry.replay_file = std::string{"/"} + std::string{cols[1]};
parse_int(cols[0], entry.replay_frequency);
parse_int(cols[2], entry.sample_rate);
if (entry.replay_frequency == 0 || entry.sample_rate == 0)
continue; // Invalid freq or rate.
if (cols.size() == 4) // Optional delay value.
parse_int(cols[3], entry.next_delay);
entry.replay_file = fs::path{u"/"} + std::string{cols[1]};
playlist_db.emplace_back(std::move(entry));
}
if (cols.size() == 4) // Optional delay value.
parse_int(cols[3], entry.initial_delay);
playlist_db_.emplace_back(std::move(entry));
}
}
total_tracks = playlist_db.size();
playlist_masterdb = playlist_db;
text_track.set(to_string_dec_uint(track_number) + "/" + to_string_dec_uint(total_tracks) + " " + now_play_list_file.string());
tracks_progressbar.set_max(total_tracks);
void PlaylistView::on_file_changed(const fs::path& new_file_path) {
stop();
reset();
playlist_path_ = new_file_path;
playlist_db_.clear();
load_file(playlist_path_);
update_ui();
button_play.focus();
}
void PlaylistView::on_file_changed(std::filesystem::path new_file_path, rf::Frequency replay_frequency, uint32_t replay_sample_rate, uint32_t next_delay) {
File data_file;
// Get file size
auto error = data_file.open("/" + new_file_path.string());
if (error) {
file_error("C16 file\n" + new_file_path.string() + "\nread error.");
return;
}
track_number = track_number >= total_tracks ? 1 : track_number + 1; // prevent track_number out of range
file_path = new_file_path;
field_frequency.set_value(replay_frequency);
sample_rate = replay_sample_rate;
text_sample_rate.set(unit_auto_scale(sample_rate, 3, 0) + "Hz");
now_delay = next_delay;
auto file_size = data_file.size();
auto duration = (file_size * 1000) / (2 * 2 * sample_rate);
on_track_progressbar.set_max(file_size);
text_filename.set(file_path.filename().string().substr(0, 12));
text_duration.set(to_string_time_ms(duration));
text_track.set(to_string_dec_uint(track_number) + "/" + to_string_dec_uint(total_tracks) + " " + now_play_list_file.string());
tracks_progressbar.set_value(track_number);
void PlaylistView::reset() {
current_track_ = 0;
current_entry_ = nullptr;
current_entry_size_ = 0;
}
void PlaylistView::on_tx_progress(const uint32_t progress) {
on_track_progressbar.set_value(progress);
}
void PlaylistView::focus() {
button_open.focus();
}
void PlaylistView::file_error(std::string error_message) {
clean_playlist();
stop(false);
nav_.display_modal("Error", "Error for \n" + file_path.string() + "\n" + error_message);
}
void PlaylistView::clean_playlist() {
total_tracks = 0;
track_number = 0;
playlist_db.clear();
playlist_masterdb.clear();
void PlaylistView::show_file_error(const fs::path& path, const std::string& message) {
nav_.display_modal("Error", "Error opening file \n" + path.string() + "\n" + message);
}
bool PlaylistView::is_active() const {
return (bool)replay_thread;
return replay_thread_ != nullptr;
}
bool PlaylistView::loop() const {
return (bool)playlist_db.size();
return check_loop.value();
}
bool PlaylistView::is_done() const {
return current_track_ >= playlist_db_.size();
}
void PlaylistView::toggle() {
if (is_active()) {
stop(false);
clean_playlist();
} else {
clean_playlist();
if (std::filesystem::file_exists(now_play_list_file.string())) {
load_file(now_play_list_file);
if (!playlist_db.empty()) {
start();
}
} else {
text_track.set("0/0 no input playlist file");
}
}
if (is_active())
stop();
else
start();
}
void PlaylistView::start() {
stop(false);
reset();
playlist_entry item = playlist_db.front();
playlist_db.pop_front();
on_file_changed(item.replay_file, item.replay_frequency, item.sample_rate, item.next_delay);
transmitter_model.set_target_frequency(item.replay_frequency);
if (playlist_db_.empty())
return; // Nothing to do.
std::unique_ptr<stream::Reader> reader;
auto p = std::make_unique<FileReader>();
auto open_error = p->open(file_path);
if (open_error.is_valid()) {
file_error("Illegal grammar");
return; // Fixes TX bug if there's a file error
} else {
reader = std::move(p);
}
if (reader) {
button_play.set_bitmap(&bitmap_stop);
if (now_delay) { // this `if` is because, if the delay is 0, it will sleep forever
chThdSleepMilliseconds(now_delay);
}
replay_thread = std::make_unique<ReplayThread>(
std::move(reader),
read_size, buffer_count,
&ready_signal,
[](uint32_t return_code) {
ReplayThreadDoneMessage message{return_code};
EventDispatcher::send_message(message);
});
}
transmitter_model.set_sampling_rate(sample_rate * 8);
transmitter_model.set_baseband_bandwidth(baseband_bandwidth);
transmitter_model.enable();
if (next_track())
send_current_track();
}
void PlaylistView::stop(const bool do_loop) {
if (is_active()) {
replay_thread.reset();
/* Advance to the next track in the playlist. */
bool PlaylistView::next_track() {
if (is_done()) {
if (loop())
current_track_ = 0;
else
return false; // All done.
}
// TODO: the logic here could be more beautiful but maybe they are all same for compiler anyway....
// Notes of the logic here in case if it needed to be changed in the future:
// 1. check_loop.value() is for the LOOP checkbox
// 2. do_loop is a part of the replay thread, not a user - control thing.
// 3. when (total_tracks > track_number) is true, it means that the current track is not the last track.
// Thus, (do_loop && (total_tracks != track_number)) is for the case when the start() func were called with true AND not the last track.
// Which means it do loop until the last track.
if (check_loop.value()) {
if (do_loop) {
if (playlist_db.size() > 0) {
start();
} else {
playlist_db = playlist_masterdb;
start();
}
} else {
transmitter_model.disable();
button_play.set_bitmap(&bitmap_play);
}
} else if (!check_loop.value()) {
if (do_loop && (total_tracks > track_number)) {
if (playlist_db.size() > 0) {
start();
} else {
playlist_db = playlist_masterdb;
start();
}
} else {
transmitter_model.disable();
button_play.set_bitmap(&bitmap_play);
}
}
ready_signal = false;
current_entry_ = &playlist_db_[current_track_];
current_track_++;
return true;
}
void PlaylistView::handle_replay_thread_done(const uint32_t return_code) {
if (return_code == ReplayThread::END_OF_FILE) {
stop(true);
} else if (return_code == ReplayThread::READ_ERROR) {
file_error("Replay thread read error");
/* Transmits the current_entry_ */
void PlaylistView::send_current_track() {
// Prepare to send a file.
replay_thread_.reset();
transmitter_model.disable();
ready_signal_ = false;
if (!current_entry_)
return;
// Open the sample file to send.
auto reader = std::make_unique<FileReader>();
auto error = reader->open(current_entry_->replay_file);
if (error) {
show_file_error(current_entry_->replay_file, "Can't open file to send.");
return;
}
on_track_progressbar.set_value(0);
// Get the sample file size for the progress bar.
current_entry_size_ = reader->file().size();
progressbar_transmit.set_value(0);
// Wait a bit before sending if specified.
// TODO: this pauses the main thread, move into ReplayThread instead.
if (current_entry_->initial_delay > 0)
chThdSleepMilliseconds(current_entry_->initial_delay);
// ReplayThread starts immediately on contruction so
// these need to be set before creating the ReplayThread.
transmitter_model.set_target_frequency(current_entry_->replay_frequency);
transmitter_model.set_sampling_rate(current_entry_->sample_rate * 8);
transmitter_model.set_baseband_bandwidth(baseband_bandwidth);
transmitter_model.enable();
// Set baseband sample rate too for waterfall to be correct.
// TODO: Why doesn't the transmitter_model just handle this?
baseband::set_sample_rate(transmitter_model.sampling_rate());
// Use the ReplayThread class to send the data.
replay_thread_ = std::make_unique<ReplayThread>(
std::move(reader),
/* read_size */ 0x4000,
/* buffer_count */ 3,
&ready_signal_,
[](uint32_t return_code) {
ReplayThreadDoneMessage message{return_code};
EventDispatcher::send_message(message);
});
// Now it's sending, update the UI.
update_ui();
}
void PlaylistView::stop() {
// This terminates the underlying chThread.
replay_thread_.reset();
transmitter_model.disable();
update_ui();
}
void PlaylistView::update_ui() {
if (playlist_db_.empty()) {
text_track.set("Open a playlist file.");
} else {
text_track.set(
to_string_dec_uint(current_track_) + "/" +
to_string_dec_uint(playlist_db_.size()) + " " +
playlist_path_.filename().string());
}
button_play.set_bitmap(is_active() ? &bitmap_stop : &bitmap_play);
progressbar_track.set_value(current_track_);
progressbar_track.set_max(playlist_db_.size());
progressbar_transmit.set_max(current_entry_size_);
if (current_entry_) {
auto duration = ms_duration(current_entry_size_, current_entry_->sample_rate, 4);
text_filename.set(truncate(current_entry_->replay_file.filename().string(), 12));
text_sample_rate.set(unit_auto_scale(current_entry_->sample_rate, 3, 0) + "Hz");
text_duration.set(to_string_time_ms(duration));
text_frequency.set(to_string_short_freq(current_entry_->replay_frequency));
} else {
text_filename.set("-");
text_sample_rate.set("-");
text_duration.set("-");
text_frequency.set("-");
}
}
void PlaylistView::on_tx_progress(uint32_t progress) {
progressbar_transmit.set_value(progress);
}
void PlaylistView::handle_replay_thread_done(uint32_t return_code) {
if (return_code == ReplayThread::END_OF_FILE) {
if (next_track()) {
send_current_track();
return;
}
}
if (return_code == ReplayThread::READ_ERROR)
show_file_error(current_entry_->replay_file, "Replay read failed.");
stop();
}
PlaylistView::PlaylistView(
@@ -261,10 +258,10 @@ PlaylistView::PlaylistView(
&text_filename,
&text_sample_rate,
&text_duration,
&tracks_progressbar,
&on_track_progressbar,
&field_frequency,
&tx_view, // this handles now the previous rfgain, rfamp
&progressbar_track,
&progressbar_transmit,
&text_frequency,
&tx_view,
&check_loop,
&button_play,
&text_track,
@@ -273,36 +270,18 @@ PlaylistView::PlaylistView(
waterfall.show_audio_spectrum_view(false);
field_frequency.set_value(transmitter_model.target_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
transmitter_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->field_frequency.set_value(f);
};
};
field_frequency.set_step(5000);
button_play.on_select = [this](ImageButton&) {
this->toggle();
toggle();
};
button_open.on_select = [this, &nav](Button&) {
auto open_view = nav.push<FileLoadView>(".PPL");
open_view->on_changed = [this](std::filesystem::path new_file_path) {
now_play_list_file = new_file_path;
if (std::filesystem::file_exists(now_play_list_file.string())) {
load_file(now_play_list_file);
} else {
text_track.set("0/0 no input playlist file");
}
on_file_changed(new_file_path);
};
};
update_ui();
}
PlaylistView::~PlaylistView() {
@@ -310,20 +289,23 @@ PlaylistView::~PlaylistView() {
baseband::shutdown();
}
void PlaylistView::set_parent_rect(Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
const ui::Rect waterfall_rect{
0, header_height, new_parent_rect.width(),
new_parent_rect.height() - header_height};
waterfall.set_parent_rect(waterfall_rect);
}
void PlaylistView::on_hide() {
stop(false);
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
// it's being shown or hidden.
stop();
waterfall.on_hide();
View::on_hide();
}
void PlaylistView::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(),
new_parent_rect.height() - header_height};
waterfall.set_parent_rect(waterfall_rect);
void PlaylistView::focus() {
button_open.focus();
}
} /* namespace ui */
+61 -61
View File
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2023 Kyle Reed, zxkmm
*
* This file is part of PortaPack.
*
@@ -34,20 +35,21 @@
#include <string>
#include <memory>
#include <deque>
#include <vector>
namespace ui {
class PlaylistView : public View {
public:
PlaylistView(NavigationView& nav);
~PlaylistView();
// Disable copy to make -Weffc++ happy.
PlaylistView(const PlaylistView&) = delete;
PlaylistView& operator=(const PlaylistView&) = delete;
void set_parent_rect(Rect new_parent_rect) override;
void on_hide() override;
void set_parent_rect(const Rect new_parent_rect) override;
void focus() override;
std::string title() const override { return "Playlist"; };
@@ -58,75 +60,73 @@ class PlaylistView : public View {
app_settings::SettingsManager settings_{
"tx_playlist", app_settings::Mode::TX};
// add or remove lines here to allow more header and less spectrum view, & vice versa
// More header == less spectrum view.
static constexpr ui::Dim header_height = 4 * 16;
static constexpr uint32_t baseband_bandwidth = 2500000;
struct playlist_entry {
rf::Frequency replay_frequency{0};
std::string replay_file{};
std::filesystem::path replay_file{};
uint32_t sample_rate{};
uint32_t next_delay{};
uint32_t initial_delay{};
};
std::deque<playlist_entry> playlist_db{};
std::deque<playlist_entry> playlist_masterdb{};
uint32_t sample_rate = 0;
int32_t tx_gain{47};
bool rf_amp{true}; // aux private var to store temporal, Replay App rf_amp user selection.
static constexpr uint32_t baseband_bandwidth = 2500000;
const size_t read_size{16384};
const size_t buffer_count{3};
void load_file(std::filesystem::path playlist_path);
void on_file_changed(std::filesystem::path new_file_path, rf::Frequency replay_frequency, uint32_t replay_sample_rate, uint32_t next_delay);
void on_tx_progress(const uint32_t progress);
void set_target_frequency(const rf::Frequency new_value);
rf::Frequency target_frequency() const;
void toggle();
void start();
void stop(const bool do_loop);
std::unique_ptr<ReplayThread> replay_thread_{};
bool ready_signal_{}; // Used to signal the ReplayThread.
size_t current_track_{0};
const playlist_entry* current_entry_{};
size_t current_entry_size_{0};
std::vector<playlist_entry> playlist_db_{};
std::filesystem::path playlist_path_{};
void load_file(const std::filesystem::path& path);
void on_file_changed(const std::filesystem::path& path);
void reset();
void show_file_error(
const std::filesystem::path& path,
const std::string& message);
bool is_active() const;
bool loop() const;
void set_ready();
void handle_replay_thread_done(const uint32_t return_code);
void file_error(std::string error_message);
void clean_playlist();
bool is_done() const;
std::filesystem::path file_path{};
std::unique_ptr<ReplayThread> replay_thread{};
bool ready_signal{false};
size_t track_number{0};
size_t total_tracks{0};
uint32_t now_delay{0};
std::filesystem::path now_play_list_file{};
void toggle();
void start();
bool next_track();
void send_current_track();
void stop();
void update_ui();
/* There are called by Message handlers. */
void on_tx_progress(uint32_t progress);
void handle_replay_thread_done(uint32_t return_code);
Button button_open{
{0 * 8, 0 * 16, 10 * 8, 2 * 16},
"Open file"};
"Open PPL"};
Text text_filename{
{11 * 8, 0 * 16, 12 * 8, 16},
"-"};
{11 * 8, 0 * 16, 12 * 8, 16}};
Text text_sample_rate{
{24 * 8, 0 * 16, 6 * 8, 16},
"-"};
{24 * 8, 0 * 16, 6 * 8, 16}};
Text text_duration{
{11 * 8, 1 * 16, 6 * 8, 16},
"-"};
ProgressBar tracks_progressbar{
{11 * 8, 1 * 16, 6 * 8, 16}};
ProgressBar progressbar_track{
{18 * 8, 1 * 16, 12 * 8, 8}};
ProgressBar on_track_progressbar{
ProgressBar progressbar_transmit{
{18 * 8, 3 * 8, 12 * 8, 8}};
FrequencyField field_frequency{
{0 * 8, 2 * 16},
};
Text text_frequency{
{0 * 8, 2 * 16, 9 * 8, 16}};
TransmitterView2 tx_view{
// new handling of NumberField field_rfgain, NumberField field_rfamp
74, 1 * 8, SHORT_UI // x(columns), y (line) position. (Used in Repay / GPS Simul / Play list App)
// 10*8, 2*8, NORMAL_UI // x(columns), y (line) position. (Used in Mic App)
74, 1 * 8, SHORT_UI // x(columns), y (line) position.
};
Checkbox check_loop{
@@ -134,6 +134,7 @@ class PlaylistView : public View {
4,
"Loop",
true};
ImageButton button_play{
{28 * 8, 2 * 16, 2 * 8, 1 * 16},
&bitmap_play,
@@ -141,32 +142,31 @@ class PlaylistView : public View {
Color::black()};
Text text_track{
{0 * 8, 3 * 16, 16 * 8, 16},
"0/0 no input playlist file"};
{0 * 8, 3 * 16, 30 * 8, 16}};
spectrum::WaterfallWidget waterfall{};
MessageHandlerRegistration message_handler_replay_thread_error{
Message::ID::ReplayThreadDone,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
this->handle_replay_thread_done(message.return_code);
[this](const Message* p) {
auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
handle_replay_thread_done(message.return_code);
}};
MessageHandlerRegistration message_handler_fifo_signal{
Message::ID::RequestSignal,
[this](const Message* const p) {
const auto message = static_cast<const RequestSignalMessage*>(p);
[this](const Message* p) {
auto message = static_cast<const RequestSignalMessage*>(p);
if (message->signal == RequestSignalMessage::Signal::FillRequest) {
this->set_ready();
ready_signal_ = true;
}
}};
MessageHandlerRegistration message_handler_tx_progress{
Message::ID::TXProgress,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
this->on_tx_progress(message.progress);
[this](const Message* p) {
auto message = *reinterpret_cast<const TXProgressMessage*>(p);
on_tx_progress(message.progress);
}};
};
+1 -3
View File
@@ -23,7 +23,6 @@
#include "ui_pocsag_tx.hpp"
#include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "string_format.hpp"
#include "ui_textentry.hpp"
@@ -40,8 +39,7 @@ void POCSAGTXView::focus() {
POCSAGTXView::~POCSAGTXView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
void POCSAGTXView::on_tx_progress(const uint32_t progress, const bool done) {
+1 -3
View File
@@ -24,7 +24,6 @@
#include "portapack.hpp"
#include "baseband_api.hpp"
#include "cpld_update.hpp"
#include "portapack_shared_memory.hpp"
#include <cstring>
@@ -161,8 +160,7 @@ void RDSView::focus() {
RDSView::~RDSView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
void RDSView::start_tx() {
+1 -1
View File
@@ -239,7 +239,7 @@ void ScannerView::handle_retune(int64_t freq, uint32_t freq_idx) {
}
void ScannerView::focus() {
field_mode.focus();
button_load.focus();
}
ScannerView::~ScannerView() {
+1 -1
View File
@@ -96,7 +96,7 @@ class ScannerView : public View {
private:
app_settings::SettingsManager settings_{
"scanner", app_settings::Mode::RX};
"rx_scanner", app_settings::Mode::RX};
NavigationView& nav_;
RxRadioState radio_state_{};
+4 -16
View File
@@ -378,26 +378,14 @@ SearchView::SearchView(
field_frequency_min.set_value(receiver_model.target_frequency() - 1000000);
field_frequency_min.set_step(100000);
field_frequency_min.on_change = [this](rf::Frequency) {
this->on_range_changed();
};
field_frequency_min.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->field_frequency_min.set_value(f);
};
field_frequency_min.updated = [this](rf::Frequency) {
on_range_changed();
};
field_frequency_max.set_value(receiver_model.target_frequency() + 1000000);
field_frequency_max.set_step(100000);
field_frequency_max.on_change = [this](rf::Frequency) {
this->on_range_changed();
};
field_frequency_max.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->field_frequency_max.set_value(f);
};
field_frequency_max.updated = [this](rf::Frequency) {
on_range_changed();
};
field_lna.set_value(receiver_model.lna());
+7 -4
View File
@@ -26,6 +26,7 @@
#include "spectrum_color_lut.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_styles.hpp"
#include "recent_entries.hpp"
@@ -146,12 +147,14 @@ class SearchView : public View {
{{1 * 8, 25 * 8}, "Accuracy +/-4.9kHz", Color::light_grey()},
{{26 * 8, 25 * 8}, "MHz", Color::light_grey()}};
FrequencyField field_frequency_min{
RxFrequencyField field_frequency_min{
{1 * 8, 1 * 16},
};
FrequencyField field_frequency_max{
nav_,
false};
RxFrequencyField field_frequency_max{
{11 * 8, 1 * 16},
};
nav_,
false};
LNAGainField field_lna{
{22 * 8, 1 * 16}};
VGAGainField field_vga{
+4 -7
View File
@@ -235,7 +235,6 @@ SetFrequencyCorrectionModel SetRadioView::form_collect() {
SetUIView::SetUIView(NavigationView& nav) {
add_children({&checkbox_disable_touchscreen,
&checkbox_speaker,
&checkbox_bloff,
&options_bloff,
&checkbox_showsplash,
@@ -246,7 +245,6 @@ SetUIView::SetUIView(NavigationView& nav) {
&button_cancel});
checkbox_disable_touchscreen.set_value(persistent_memory::disable_touchscreen());
checkbox_speaker.set_value(persistent_memory::config_speaker());
checkbox_showsplash.set_value(persistent_memory::config_splash());
checkbox_showclock.set_value(!persistent_memory::hide_clock());
checkbox_guireturnflag.set_value(persistent_memory::show_gui_return_icon());
@@ -272,11 +270,6 @@ SetUIView::SetUIView(NavigationView& nav) {
persistent_memory::set_clock_with_date(false);
}
if (checkbox_speaker.value()) audio::output::speaker_mute(); // Just mute audio if speaker is disabled
persistent_memory::set_config_speaker(checkbox_speaker.value()); // Store Speaker status
StatusRefreshMessage message{}; // Refresh status bar with/out speaker
EventDispatcher::send_message(message);
persistent_memory::set_config_splash(checkbox_showsplash.value());
persistent_memory::set_clock_hidden(!checkbox_showclock.value());
persistent_memory::set_gui_return_icon(checkbox_guireturnflag.value());
@@ -524,13 +517,17 @@ void SetPersistentMemoryView::focus() {
SetAudioView::SetAudioView(NavigationView& nav) {
add_children({&labels,
&field_tone_mix,
&checkbox_speaker_disable,
&button_save,
&button_cancel});
field_tone_mix.set_value(persistent_memory::tone_mix());
checkbox_speaker_disable.set_value(persistent_memory::config_speaker_disable());
button_save.on_select = [&nav, this](Button&) {
persistent_memory::set_tone_mix(field_tone_mix.value());
persistent_memory::set_config_speaker_disable(checkbox_speaker_disable.value());
audio::output::update_audio_mute();
nav.pop();
};
+12 -12
View File
@@ -212,17 +212,12 @@ class SetUIView : public View {
20,
"Disable touchscreen"};
Checkbox checkbox_speaker{
{3 * 8, 4 * 16},
20,
"Hide H1 Speaker option"};
Checkbox checkbox_bloff{
{3 * 8, 6 * 16},
{3 * 8, 4 * 16},
20,
"Backlight off after:"};
OptionsField options_bloff{
{52, 7 * 16 + 8},
{60, 5 * 16 + 8},
20,
{
{"5 seconds", backlight_timeout_t::Timeout5Sec},
@@ -236,25 +231,25 @@ class SetUIView : public View {
}};
Checkbox checkbox_showsplash{
{3 * 8, 9 * 16},
{3 * 8, 7 * 16},
20,
"Show splash"};
Checkbox checkbox_showclock{
{3 * 8, 11 * 16},
{3 * 8, 9 * 16},
20,
"Show clock with:"};
OptionsField options_clockformat{
{52, 12 * 16 + 8},
{60, 10 * 16 + 8},
20,
{{"time only", 0},
{"time and date", 1}}};
Checkbox checkbox_guireturnflag{
{3 * 8, 14 * 16},
{3 * 8, 12 * 16},
25,
"add return icon in GUI"};
"Show return icon in GUI"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
@@ -393,6 +388,11 @@ class SetAudioView : public View {
1,
'0'};
Checkbox checkbox_speaker_disable{
{2 * 8, 6 * 16},
25,
"Disable AK speaker amp"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
+4 -15
View File
@@ -41,7 +41,8 @@ void SondeLogger::on_packet(const sonde::Packet& packet) {
namespace ui {
SondeView::SondeView(NavigationView& nav) {
SondeView::SondeView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_sonde);
add_children({&labels,
@@ -66,20 +67,9 @@ SondeView::SondeView(NavigationView& nav) {
&button_see_map});
if (!settings_.loaded())
receiver_model.set_target_frequency(initial_target_frequency);
field_frequency.set_value(initial_target_frequency);
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.set_step(500); // euquiq: was 10000, but we are using this for fine-tunning
field_frequency.on_change = [this](rf::Frequency f) {
receiver_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
geopos.set_read_only(true);
@@ -117,7 +107,6 @@ SondeView::SondeView(NavigationView& nav) {
logger->append(LOG_ROOT_DIR "/SONDE.TXT");
audio::output::start();
audio::output::speaker_unmute();
// inject a PitchRSSIConfigureMessage in order to arm
// the pitch rssi events that will be used by the
@@ -138,7 +127,7 @@ SondeView::~SondeView() {
}
void SondeView::focus() {
field_vga.focus();
field_frequency.focus();
}
// used to convert float to character pointer, since unfortunately function like
+5 -4
View File
@@ -25,6 +25,7 @@
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_rssi.hpp"
#include "ui_qrcode.hpp"
#include "ui_geomap.hpp"
@@ -65,6 +66,7 @@ class SondeView : public View {
std::string title() const override { return "Radiosnd RX"; };
private:
NavigationView& nav_;
RxRadioState radio_state_{
1750000 /* bandwidth */,
2457600 /* sampling rate */
@@ -96,9 +98,9 @@ class SondeView : public View {
{{4 * 8, 7 * 16}, "Temp:", Color::light_grey()},
{{0 * 8, 8 * 16}, "Humidity:", Color::light_grey()}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{0 * 8, 0 * 8},
};
nav_};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
@@ -110,8 +112,7 @@ class SondeView : public View {
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4},
};
{21 * 8, 0, 6 * 8, 4}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
@@ -20,7 +20,6 @@
*/
#include "ui_spectrum_painter.hpp"
#include "cpld_update.hpp"
#include "bmp.hpp"
#include "baseband_api.hpp"
@@ -58,17 +57,7 @@ SpectrumPainterView::SpectrumPainterView(
input_image.set_parent_rect(view_rect);
input_text.set_parent_rect(view_rect);
field_frequency.set_value(transmitter_model.target_frequency());
field_frequency.set_step(5000);
field_frequency.on_change = [this](rf::Frequency f) {
transmitter_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
tx_gain = transmitter_model.tx_gain();
field_rfgain.set_value(tx_gain); // Initial default value (-12 dB's max ).
@@ -176,7 +165,6 @@ void SpectrumPainterView::frame_sync() {
SpectrumPainterView::~SpectrumPainterView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify();
baseband::shutdown();
}
@@ -94,9 +94,9 @@ class SpectrumPainterView : public View {
{{1 * 8, footer_location + 2 * 16}, "BW: Du: P:", Color::light_grey()},
};
FrequencyField field_frequency{
TxFrequencyField field_frequency{
{0 * 8, footer_location + 1 * 16},
};
nav_};
NumberField field_rfgain{
{14 * 8, footer_location + 1 * 16},
+1 -3
View File
@@ -25,7 +25,6 @@
#include "portapack.hpp"
#include "hackrf_hal.hpp"
#include "cpld_update.hpp"
#include <cstring>
#include <stdio.h>
@@ -89,8 +88,7 @@ void SSTVTXView::paint(Painter&) {
SSTVTXView::~SSTVTXView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
void SSTVTXView::prepare_scanline() {
+2 -12
View File
@@ -40,7 +40,8 @@ void TestLogger::log_raw_data(const testapp::Packet& packet, const int32_t alt)
namespace ui {
TestView::TestView(NavigationView& nav) {
TestView::TestView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_test);
add_children({&labels,
@@ -54,18 +55,7 @@ TestView::TestView(NavigationView& nav) {
&button_cal,
&check_log});
field_frequency.set_value(receiver_model.target_frequency());
field_frequency.set_step(10000);
field_frequency.on_change = [this](rf::Frequency f) {
receiver_model.set_target_frequency(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
field_frequency.set_value(f);
};
};
check_log.on_select = [this](Checkbox&, bool v) {
logging = v;
+4 -2
View File
@@ -25,6 +25,7 @@
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_rssi.hpp"
#include "event_m0.hpp"
@@ -59,6 +60,7 @@ class TestView : public View {
std::string title() const override { return "Test app"; };
private:
NavigationView& nav_;
RxRadioState radio_state_{
1750000 /* bandwidth */,
2457600 * 2 /* sampling rate */
@@ -75,9 +77,9 @@ class TestView : public View {
Labels labels{
{{0 * 8, 1 * 16}, "Data:", Color::light_grey()}};
FrequencyField field_frequency{
RxFrequencyField field_frequency{
{0 * 8, 0 * 8},
};
nav_};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
+1 -3
View File
@@ -26,7 +26,6 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "cpld_update.hpp"
using namespace portapack;
using namespace encoders;
@@ -39,8 +38,7 @@ void TouchTunesView::focus() {
TouchTunesView::~TouchTunesView() {
transmitter_model.disable();
hackrf::cpld::load_sram_no_verify(); // to leave all RX ok, without ghost signal problem at the exit.
baseband::shutdown(); // better this function at the end, not load_sram() that sometimes produces hang up.
baseband::shutdown();
}
void TouchTunesView::stop_tx() {
+4 -11
View File
@@ -102,7 +102,8 @@ void WhipCalcView::update_result() {
}
}
WhipCalcView::WhipCalcView(NavigationView& nav) {
WhipCalcView::WhipCalcView(NavigationView& nav)
: nav_{nav} {
add_children({&labels,
//&antennas_on_memory,
&field_frequency,
@@ -142,16 +143,8 @@ WhipCalcView::WhipCalcView(NavigationView& nav) {
};
field_frequency.set_step(1000000); // 1MHz step
field_frequency.on_change = [this](rf::Frequency) {
this->update_result();
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.target_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->field_frequency.set_value(f);
this->update_result();
};
field_frequency.updated = [this](rf::Frequency) {
update_result();
};
button_exit.on_select = [this, &nav](Button&) {
+5 -2
View File
@@ -26,6 +26,7 @@
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_navigation.hpp"
#include "string_format.hpp"
#include <vector>
@@ -47,6 +48,7 @@ class WhipCalcView : public View {
std::vector<uint16_t> elements{};
};
NavigationView& nav_;
std::vector<antenna_entry> antenna_db{};
void update_result();
uint16_t string_to_number(std::string);
@@ -59,9 +61,10 @@ class WhipCalcView : public View {
{{5 * 8, 3 * 16}, "Metric:", Color::light_grey()},
{{3 * 8, 4 * 16}, "Imperial:", Color::light_grey()}};
FrequencyField field_frequency{
TxFrequencyField field_frequency{
{13 * 8, 1 * 16},
};
nav_,
false};
OptionsField options_type{
{13 * 8, 2 * 16},
+38 -11
View File
@@ -134,6 +134,10 @@ static audio::Codec* audio_codec = nullptr;
namespace output {
static bool cfg_speaker_disable = false;
static bool nav_requested_mute = false;
static bool app_requested_mute = false;
void start() {
i2s::i2s0::tx_start();
unmute();
@@ -145,23 +149,46 @@ void stop() {
}
void mute() {
app_requested_mute = true;
i2s::i2s0::tx_mute();
audio_codec->headphone_disable();
}
void unmute() {
i2s::i2s0::tx_unmute();
audio_codec->headphone_enable();
}
void speaker_mute() {
i2s::i2s0::tx_mute();
audio_codec->speaker_disable();
}
void unmute() {
app_requested_mute = false;
if (!nav_requested_mute) {
i2s::i2s0::tx_unmute();
audio_codec->headphone_enable();
if (!cfg_speaker_disable) {
audio_codec->speaker_enable();
}
}
}
// The following functions are used by the navigation-bar Speaker Mute only,
// and override all other audio mute/unmute requests from apps
void speaker_mute() {
nav_requested_mute = true;
i2s::i2s0::tx_mute();
audio_codec->speaker_disable();
audio_codec->headphone_disable();
}
void speaker_unmute() {
i2s::i2s0::tx_unmute();
audio_codec->speaker_enable();
nav_requested_mute = false;
if (!app_requested_mute) {
unmute();
}
}
void update_audio_mute() {
cfg_speaker_disable = portapack::persistent_memory::config_speaker_disable();
if (portapack::persistent_memory::config_audio_mute())
speaker_mute();
else
speaker_unmute();
}
} /* namespace output */
+3
View File
@@ -65,8 +65,11 @@ void stop();
void mute();
void unmute();
void speaker_disable();
void speaker_enable();
void speaker_mute();
void speaker_unmute();
void update_audio_mute();
} /* namespace output */
+4 -4
View File
@@ -22,17 +22,17 @@
#include "io_file.hpp"
File::Result<File::Size> FileReader::read(void* const buffer, const File::Size bytes) {
auto read_result = file.read(buffer, bytes);
auto read_result = file_.read(buffer, bytes);
if (read_result.is_ok()) {
bytes_read += read_result.value();
bytes_read_ += read_result.value();
}
return read_result;
}
File::Result<File::Size> FileWriter::write(const void* const buffer, const File::Size bytes) {
auto write_result = file.write(buffer, bytes);
auto write_result = file_.write(buffer, bytes);
if (write_result.is_ok()) {
bytes_written += write_result.value();
bytes_written_ += write_result.value();
}
return write_result;
}
+8 -6
View File
@@ -38,14 +38,15 @@ class FileReader : public stream::Reader {
FileReader& operator=(FileReader&&) = delete;
Optional<File::Error> open(const std::filesystem::path& filename) {
return file.open(filename);
return file_.open(filename);
}
File::Result<File::Size> read(void* const buffer, const File::Size bytes) override;
const File& file() const& { return file_; }
protected:
File file{};
uint64_t bytes_read{0};
File file_{};
uint64_t bytes_read_{0};
};
class FileWriter : public stream::Writer {
@@ -58,14 +59,15 @@ class FileWriter : public stream::Writer {
FileWriter& operator=(FileWriter&&) = delete;
Optional<File::Error> create(const std::filesystem::path& filename) {
return file.create(filename);
return file_.create(filename);
}
File::Result<File::Size> write(const void* const buffer, const File::Size bytes) override;
const File& file() const& { return file_; }
protected:
File file{};
uint64_t bytes_written{0};
File file_{};
uint64_t bytes_written_{0};
};
using RawFileWriter = FileWriter;
+16 -15
View File
@@ -21,6 +21,7 @@
*/
#include "io_wave.hpp"
#include "utility.hpp"
bool WAVFileReader::open(const std::filesystem::path& path) {
size_t i = 0;
@@ -36,10 +37,10 @@ bool WAVFileReader::open(const std::filesystem::path& path) {
return true;
}
auto error = file.open(path);
auto error = file_.open(path);
if (!error.is_valid()) {
file.read((void*)&header, sizeof(header)); // Read header (RIFF and WAVE)
file_.read((void*)&header, sizeof(header)); // Read header (RIFF and WAVE)
riff_size = header.cksize + 8;
data_start = header.fmt.cksize + 28;
@@ -48,14 +49,14 @@ bool WAVFileReader::open(const std::filesystem::path& path) {
// Look for INAM (title) tag
if (data_end < riff_size) {
file.seek(data_end);
while (file.read((void*)&ch, 1).is_ok()) {
file_.seek(data_end);
while (file_.read((void*)&ch, 1).is_ok()) {
if (ch == tag_INAM[i++]) {
if (i == 4) {
// Tag found, copy title
file.read((void*)&title_size, sizeof(uint32_t));
file_.read((void*)&title_size, sizeof(uint32_t));
if (title_size > 32) title_size = 32;
file.read((void*)&title_buffer, title_size);
file_.read((void*)&title_buffer, title_size);
title_string = title_buffer;
break;
}
@@ -86,7 +87,7 @@ bool WAVFileReader::open(const std::filesystem::path& path) {
}
void WAVFileReader::rewind() {
file.seek(data_start);
file_.seek(data_start);
}
std::string WAVFileReader::title() {
@@ -94,15 +95,15 @@ std::string WAVFileReader::title() {
}
uint32_t WAVFileReader::ms_duration() {
return ((data_size_ * 1000) / sample_rate_) / bytes_per_sample;
return ::ms_duration(data_size_, sample_rate_, bytes_per_sample);
}
void WAVFileReader::data_seek(const uint64_t Offset) {
file.seek(data_start + (Offset * bytes_per_sample));
file_.seek(data_start + (Offset * bytes_per_sample));
}
/*int WAVFileReader::seek_mss(const uint16_t minutes, const uint8_t seconds, const uint32_t samples) {
const auto result = file.seek(data_start + ((((minutes * 60) + seconds) * sample_rate_) + samples) * bytes_per_sample);
const auto result = file_.seek(data_start + ((((minutes * 60) + seconds) * sample_rate_) + samples) * bytes_per_sample);
if (result.is_error())
return 0;
@@ -145,21 +146,21 @@ Optional<File::Error> WAVFileWriter::create(
}
Optional<File::Error> WAVFileWriter::update_header() {
header_t header{sampling_rate, (uint32_t)bytes_written - sizeof(header_t), info_chunk_size};
header_t header{sampling_rate, (uint32_t)bytes_written_ - sizeof(header_t), info_chunk_size};
const auto seek_0_result = file.seek(0);
const auto seek_0_result = file_.seek(0);
if (seek_0_result.is_error()) {
return seek_0_result.error();
}
const auto old_position = seek_0_result.value();
const auto write_result = file.write(&header, sizeof(header));
const auto write_result = file_.write(&header, sizeof(header));
if (write_result.is_error()) {
return write_result.error();
}
const auto seek_old_result = file.seek(old_position);
const auto seek_old_result = file_.seek(old_position);
if (seek_old_result.is_error()) {
return seek_old_result.error();
}
@@ -170,7 +171,7 @@ Optional<File::Error> WAVFileWriter::update_header() {
Optional<File::Error> WAVFileWriter::write_tags() {
tags_t tags{title};
const auto write_result = file.write(&tags, sizeof(tags));
const auto write_result = file_.write(&tags, sizeof(tags));
if (write_result.is_error()) {
return write_result.error();
}
-9
View File
@@ -96,15 +96,6 @@ bool get_antenna_bias() {
return antenna_bias;
}
bool speaker_mode{false};
void set_speaker_mode(const bool v) {
speaker_mode = v;
if (speaker_mode)
audio::output::speaker_unmute();
else
audio::output::speaker_mute();
}
static constexpr uint32_t systick_count(const uint32_t clock_source_f) {
return clock_source_f / CH_FREQUENCY;
}
-3
View File
@@ -53,9 +53,6 @@ extern ClockManager clock_manager;
extern ReceiverModel receiver_model;
extern TransmitterModel transmitter_model;
extern bool speaker_mode;
void set_speaker_mode(const bool v);
extern uint32_t bl_tick_counter;
extern bool antenna_bias;
+9 -8
View File
@@ -92,7 +92,8 @@ max2839::MAX2839 second_if_max2839{ssp1_target_max283x};
static max5864::MAX5864 baseband_codec{ssp1_target_max5864};
static baseband::CPLD baseband_cpld;
static rf::Direction direction{rf::Direction::Receive};
// Set invalid to force the set_direction CPLD workaround to run.
static rf::Direction direction{-1};
static bool baseband_invert = false;
static bool mixer_invert = false;
@@ -115,15 +116,15 @@ void set_direction(const rf::Direction new_direction) {
/* TODO: Refactor all the various "Direction" enumerations into one. */
/* TODO: Only make changes if direction changes, but beware of clock enabling. */
// Hack to fix the CPLD (clocking ?) bug: toggle CPLD SRAM overlay depending on new direction
// Hack to fix the CPLD (clocking ?) bug: toggle CPLD SRAM overlay depending on new direction.
// Use CPLD's EEPROM config when transmitting
// Use the SRAM overlay when receiving
// teixeluis: undone "Hack to fix the CPLD (clocking ?) bug".
// Apparently with current CPLD code from the hackrf repo,
// toggling CPLD overlay should no longer be necessary:
if (direction != new_direction && new_direction == rf::Direction::Transmit) {
hackrf::cpld::init_from_eeprom();
if (direction != new_direction) {
if (new_direction == rf::Direction::Transmit)
hackrf::cpld::init_from_eeprom();
else
// Prevents ghosting when switching back to RX from TX mode.
hackrf::cpld::load_sram_no_verify();
}
direction = new_direction;
+11 -4
View File
@@ -30,6 +30,12 @@
/* Stashes current radio bandwidth and sampling rate.
* Inits to defaults, and restores previous when destroyed.
* The idea here is that an app will eventually call enable
* on the model which will sync all of the model state into
* the radio. We tried lazily setting these using the
* set_configuration_without_update function, but there are
* still various UI components (mainly Waterfall) that need
* the radio set in order to load properly.
* NB: This member must be added before SettingsManager. */
template <typename TModel, TModel* model>
class RadioState {
@@ -41,13 +47,14 @@ class RadioState {
RadioState(uint32_t new_bandwidth, uint32_t new_sampling_rate)
: prev_bandwidth_{model->baseband_bandwidth()},
prev_sampling_rate_{model->sampling_rate()} {
model->set_baseband_bandwidth(new_bandwidth);
// Update the new settings in the radio.
model->set_sampling_rate(new_sampling_rate);
model->set_baseband_bandwidth(new_bandwidth);
}
~RadioState() {
model->set_baseband_bandwidth(prev_bandwidth_);
model->set_sampling_rate(prev_sampling_rate_);
model->set_configuration_without_update(
prev_bandwidth_, prev_sampling_rate_);
}
private:
@@ -58,4 +65,4 @@ class RadioState {
using RxRadioState = RadioState<ReceiverModel, &portapack::receiver_model>;
using TxRadioState = RadioState<TransmitterModel, &portapack::transmitter_model>;
#endif // __RADIO_STATE_H__
#endif // __RADIO_STATE_H__
+13 -6
View File
@@ -248,12 +248,19 @@ void ReceiverModel::set_wfm_configuration(const size_t n) {
}
}
void ReceiverModel::set_configuration_without_init(
const Mode new_mode,
const rf::Frequency new_frequency_step,
const size_t new_am_config_index,
const size_t new_nbfm_config_index,
const size_t new_wfm_config_index,
void ReceiverModel::set_configuration_without_update(
uint32_t baseband_bandwidth,
uint32_t sampling_rate) {
baseband_bandwidth_ = baseband_bandwidth;
sampling_rate_ = sampling_rate;
}
void ReceiverModel::set_configuration_without_update(
Mode new_mode,
rf::Frequency new_frequency_step,
size_t new_am_config_index,
size_t new_nbfm_config_index,
size_t new_wfm_config_index,
uint8_t new_squelch_level) {
mode_ = new_mode;
frequency_step_ = new_frequency_step;
+11 -6
View File
@@ -92,12 +92,17 @@ class ReceiverModel {
size_t wfm_configuration() const;
void set_wfm_configuration(const size_t n);
void set_configuration_without_init(
const Mode new_mode,
const rf::Frequency new_frequency_step,
const size_t new_am_config_index,
const size_t new_nbfm_config_index,
const size_t new_wfm_config_index,
/* Sets the model values without updating the radio. */
void set_configuration_without_update(
uint32_t baseband_bandwidth,
uint32_t sampling_rate);
void set_configuration_without_update(
Mode new_mode,
rf::Frequency new_frequency_step,
size_t new_am_config_index,
size_t new_nbfm_config_index,
size_t new_wfm_config_index,
uint8_t new_squelch_level);
void configure_from_app_settings(const app_settings::AppSettings& settings);
@@ -147,6 +147,13 @@ void TransmitterModel::disable() {
led_tx.off();
}
void TransmitterModel::set_configuration_without_update(
uint32_t baseband_bandwidth,
uint32_t sampling_rate) {
baseband_bandwidth_ = baseband_bandwidth;
sampling_rate_ = sampling_rate;
}
void TransmitterModel::configure_from_app_settings(
const app_settings::AppSettings& settings) {
baseband_bandwidth_ = settings.baseband_bandwidth;
@@ -70,6 +70,11 @@ class TransmitterModel {
void enable();
void disable();
/* Sets the model values without updating the radio. */
void set_configuration_without_update(
uint32_t baseband_bandwidth,
uint32_t sampling_rate);
void configure_from_app_settings(const app_settings::AppSettings& settings);
private:
+72
View File
@@ -0,0 +1,72 @@
/*
* Copyright (C) 2023 Kyle Reed
*
* 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 __UI_FREQ_FIELD_H__
#define __UI_FREQ_FIELD_H__
#include "portapack.hpp"
#include "receiver_model.hpp"
#include "transmitter_model.hpp"
#include "ui_receiver.hpp"
namespace ui {
/* A frequency field bound directly a radio model
* that can manage its own editing UX. */
template <typename TModel, TModel* model>
class BoundFrequencyField : public FrequencyField {
private:
using FrequencyField::on_change;
using FrequencyField::on_edit;
public:
decltype(FrequencyField::on_change) updated{};
BoundFrequencyField(Point parent_pos, NavigationView& nav, bool update_model = true)
: FrequencyField(parent_pos) {
// NB: There is no frequency_step on the tx_model.
set_step(portapack::receiver_model.frequency_step());
set_value(model->target_frequency());
on_change = [this, update_model](rf::Frequency f) {
if (update_model)
model->set_target_frequency(f);
if (updated)
updated(f);
};
on_edit = [this, &nav]() {
auto freq_view = nav.push<FrequencyKeypadView>(model->target_frequency());
freq_view->on_changed = [this](rf::Frequency f) {
set_value(f);
};
};
}
// TODO: override set_step and update the rx model then call base.
};
using RxFrequencyField = BoundFrequencyField<ReceiverModel, &portapack::receiver_model>;
using TxFrequencyField = BoundFrequencyField<TransmitterModel, &portapack::transmitter_model>;
} // namespace ui
#endif // __UI_FREQ_FIELD_H__
+4 -5
View File
@@ -36,7 +36,6 @@
namespace ui {
// TODO: build in support for editing.
class FrequencyField : public Widget {
public:
std::function<void(rf::Frequency)> on_change{};
@@ -45,7 +44,7 @@ class FrequencyField : public Widget {
using range_t = rf::FrequencyRange;
FrequencyField(const Point parent_pos);
FrequencyField(Point parent_pos);
rf::Frequency value() const;
@@ -54,9 +53,9 @@ class FrequencyField : public Widget {
void paint(Painter& painter) override;
bool on_key(const ui::KeyEvent event) override;
bool on_encoder(const EncoderEvent delta) override;
bool on_touch(const TouchEvent event) override;
bool on_key(ui::KeyEvent event) override;
bool on_encoder(EncoderEvent delta) override;
bool on_touch(TouchEvent event) override;
void on_focus() override;
private:
+2
View File
@@ -311,10 +311,12 @@ WaterfallWidget::WaterfallWidget(const bool cursor) {
}
void WaterfallWidget::on_show() {
// TODO: Assert that baseband is not shutdown.
baseband::spectrum_streaming_start();
}
void WaterfallWidget::on_hide() {
// TODO: Assert that baseband is not shutdown.
baseband::spectrum_streaming_stop();
}
+4
View File
@@ -104,6 +104,10 @@ class FrequencyScale : public Widget {
void draw_filter_ranges(Painter& painter, const Rect r);
};
/* NB: These visualizations rely on having a baseband image running.
* If the baseband is shutdown or otherwise not running when interacting
* with these, they will almost certainly hang the device. */
class WaterfallView : public Widget {
public:
void on_show() override;
+11 -21
View File
@@ -126,11 +126,6 @@ SystemStatusView::SystemStatusView(
portapack::persistent_memory::load_persistent_settings_from_file();
}
if (portapack::persistent_memory::config_speaker())
button_speaker.hidden(false);
else
button_speaker.hidden(true);
if (portapack::persistent_memory::config_hide_converter()) {
button_converter.hidden(true);
} else {
@@ -199,6 +194,8 @@ SystemStatusView::SystemStatusView(
button_clock_status.on_select = [this](ImageButton&) {
this->on_clk();
};
audio::output::update_audio_mute();
}
void SystemStatusView::refresh() {
@@ -217,16 +214,13 @@ void SystemStatusView::refresh() {
button_converter.set_foreground(Color::light_grey());
}
}
// Poke tunings to take converter change in account.
receiver_model.set_target_frequency(receiver_model.target_frequency());
transmitter_model.set_target_frequency(transmitter_model.target_frequency());
if (!portapack::persistent_memory::config_speaker()) {
if (portapack::persistent_memory::config_audio_mute()) {
button_speaker.set_foreground(Color::light_grey());
button_speaker.set_bitmap(&bitmap_icon_speaker_mute);
button_speaker.hidden(false);
} else {
button_speaker.hidden(true);
button_speaker.set_foreground(Color::green());
button_speaker.set_bitmap(&bitmap_icon_speaker);
}
if (portapack::get_antenna_bias()) {
@@ -285,20 +279,16 @@ void SystemStatusView::on_converter() {
button_converter.set_foreground(Color::light_grey());
}
// Poke to update tuning.
// Poke to update tuning
// NOTE: Code assumes here that a TX app isn't active, since RX & TX have diff tuning offsets
// (and there's only one tuner in the radio so can't update tuner for both).
receiver_model.set_target_frequency(receiver_model.target_frequency());
}
void SystemStatusView::on_speaker() {
if (!portapack::speaker_mode) {
portapack::set_speaker_mode(true);
button_speaker.set_foreground(Color::green());
button_speaker.set_bitmap(&bitmap_icon_speaker);
} else {
portapack::set_speaker_mode(false);
button_speaker.set_foreground(Color::light_grey());
button_speaker.set_bitmap(&bitmap_icon_speaker_mute);
}
portapack::persistent_memory::set_config_audio_mute(!portapack::persistent_memory::config_audio_mute());
audio::output::update_audio_mute();
refresh();
}
void SystemStatusView::on_stealth() {
+10 -2
View File
@@ -175,7 +175,11 @@ void AK4951::headphone_enable() {
void AK4951::headphone_disable() {
set_headphone_power(false);
set_dac_power(false);
// Don't power off DAC unless Speaker is disabled also
if (map.r.power_management_2.PMSL == 0) {
set_dac_power(false);
}
}
void AK4951::speaker_enable() {
@@ -210,7 +214,11 @@ void AK4951::speaker_disable() {
update(Register::SignalSelect1);
// Power down DAC, Programmable Filter and speaker: PMDAC=PMPFIL=PMSL bits= “1”→“0”
set_dac_power(false);
// Exception: Don't power off DAC unless Headphones are disabled too
if (map.r.power_management_2.PMHPL == 0) {
set_dac_power(false);
}
// map.r.power_management_1.PMPFIL = 0;
// update(Register::PowerManagement1);
set_speaker_power(false);
+4 -4
View File
@@ -113,10 +113,10 @@ bool load_sram() {
}
void load_sram_no_verify() {
// CoolRunner II family has Hybrid memory CPLD arquitecture (SRAM+NVM)
// It seems that after using TX App somehow , I do not why , the CPLD_SRAM part needs to be re_loaded to solve #637 ghost beat
// load_sram() it is already called at each boot in portapack.cpp ,including verify CPLD part.
// Here we skipped CPLD verify part,just to be quicker (in case any CPLD problem it will be detected in the boot process).
// CoolRunner II family has Hybrid memory CPLD architecture (SRAM+NVM)
// It seems that after using a TX App the CPLD_SRAM part needs to be re_loaded to solve #637 ghost beat.
// load_sram() it is already called at each boot in portapack.cpp, including verify CPLD part.
// Here we skipped CPLD verify part, just to be quicker (in case any CPLD problem it will be detected in the boot process).
auto jtag_target_hackrf_cpld = jtag_target_hackrf();
hackrf::one::cpld::CPLD hackrf_cpld{jtag_target_hackrf_cpld};
+1 -1
View File
@@ -44,7 +44,7 @@ namespace hackrf {
namespace cpld {
bool load_sram();
void load_sram_no_verify(); // added to solve issue #637 , "ghost" signal at RX , after using any TX App
void load_sram_no_verify(); // Added to solve issue #637, "ghost" signal at RX, after using any TX App.
bool verify_eeprom();
void init_from_eeprom();
+64 -15
View File
@@ -97,7 +97,7 @@ struct ui_config_t {
HideClock = 25,
ClockWithDate = 26,
ClkOutEnabled = 27,
ConfigSpeaker = 28,
ConfigSpeakerHidden = 28,
StealthMode = 29,
ConfigLogin = 30,
ConfigSplash = 31,
@@ -216,14 +216,6 @@ struct ui_config_t {
bit_write(bits_t::ClkOutEnabled, v);
}
constexpr bool config_speaker() const {
return bit_read(bits_t::ConfigSpeaker);
}
constexpr void set_config_speaker(bool v) {
bit_write(bits_t::ConfigSpeaker, v);
}
constexpr bool stealth_mode() const {
return bit_read(bits_t::StealthMode);
}
@@ -250,7 +242,52 @@ struct ui_config_t {
constexpr ui_config_t()
: values(
(1 << ConfigSplash) | (1 << ConfigSpeaker) | (clkout_freq_reset_value << ClkoutFreqLSB) | (7 << BacklightTimeoutLSB)) {
(1 << ConfigSplash) | (clkout_freq_reset_value << ClkoutFreqLSB) | (7 << BacklightTimeoutLSB)) {
}
};
struct misc_config_t {
private:
enum bits_t {
ConfigAudioMute = 0,
ConfigSpeakerDisable = 1,
};
// misc_config_t bits:
// ConfigAudioMute = set to mute all audio output (speakers & headphones)
// ConfigSpeakerDisable = set to disable only the speaker and leave headphones enabled (only supported on AK4951 codec)
uint32_t values;
constexpr bool bit_read(const bits_t n) const {
return ((values >> n) & 1) != 0;
}
constexpr void bit_write(const bits_t n, const bool v) {
if (bit_read(n) != v) {
values ^= 1 << n;
}
}
public:
constexpr bool config_audio_mute() const {
return bit_read(bits_t::ConfigAudioMute);
}
constexpr void set_config_audio_mute(bool v) {
bit_write(bits_t::ConfigAudioMute, v);
}
constexpr bool config_speaker_disable() const {
return bit_read(bits_t::ConfigSpeakerDisable);
}
constexpr void set_config_speaker_disable(bool v) {
bit_write(bits_t::ConfigSpeakerDisable, v);
}
constexpr misc_config_t()
: values(0) {
}
};
@@ -311,6 +348,9 @@ struct data_t {
// Headphone volume in centibels.
int32_t headphone_volume_cb;
// Misc flags
misc_config_t misc_config;
constexpr data_t()
: structure_version(data_structure_version_enum::VERSION_CURRENT),
target_frequency(target_frequency_reset_value),
@@ -348,7 +388,8 @@ struct data_t {
frequency_tx_correction(0),
updown_frequency_tx_correction(0),
encoder_dial_sensitivity(0),
headphone_volume_cb(-600) {
headphone_volume_cb(-600),
misc_config() {
}
};
@@ -608,8 +649,12 @@ bool clkout_enabled() {
return data->ui_config.clkout_enabled();
}
bool config_speaker() {
return data->ui_config.config_speaker();
bool config_audio_mute() {
return data->misc_config.config_audio_mute();
}
bool config_speaker_disable() {
return data->misc_config.config_speaker_disable();
}
bool stealth_mode() {
@@ -664,8 +709,12 @@ void set_clkout_enabled(bool v) {
data->ui_config.set_clkout_enabled(v);
}
void set_config_speaker(bool v) {
data->ui_config.set_config_speaker(v);
void set_config_audio_mute(bool v) {
data->misc_config.set_config_audio_mute(v);
}
void set_config_speaker_disable(bool v) {
data->misc_config.set_config_speaker_disable(v);
}
void set_stealth_mode(bool v) {
@@ -184,7 +184,8 @@ bool show_bigger_qr_code();
bool hide_clock();
bool clock_with_date();
bool config_login();
bool config_speaker();
bool config_audio_mute();
bool config_speaker_disable();
backlight_config_t config_backlight_timer();
bool disable_touchscreen();
@@ -212,7 +213,8 @@ void set_config_freq_rx_correction(uint32_t v);
void set_clock_hidden(bool v);
void set_clock_with_date(bool v);
void set_config_login(bool v);
void set_config_speaker(bool v);
void set_config_audio_mute(bool v);
void set_config_speaker_disable(bool v);
void set_config_backlight_timer(const backlight_config_t& new_value);
void set_disable_touchscreen(bool v);
uint8_t config_encoder_dial_sensitivity();
+15
View File
@@ -90,6 +90,21 @@ inline float magnitude_squared(const std::complex<float> c) {
return r2 + i2;
}
/* Compute the duration in ms of a buffer. */
inline uint32_t ms_duration(
uint64_t buffer_size,
uint32_t sample_rate,
uint32_t bytes_per_sample) {
/* bytes * sample * second * ms
* ------ ------- ------
* bytes samples seconds
*/
if (sample_rate == 0 || bytes_per_sample == 0)
return 0;
return buffer_size / bytes_per_sample / sample_rate * 1000;
}
int fast_int_magnitude(int y, int x);
int int_atan2(int y, int x);
int32_t int16_sin_s4(int32_t x);
+8 -4
View File
@@ -306,8 +306,7 @@ class WM8731 : public audio::Codec {
});
}
void set_headphone_volume(const volume_t volume) override {
headphone_volume = volume;
void set_wm_headphone_volume(const volume_t volume) {
const auto normalized = headphone_gain_range().normalize(volume);
auto n = normalized.centibel() / 10;
@@ -319,6 +318,11 @@ class WM8731 : public audio::Codec {
});
}
void set_headphone_volume(const volume_t volume) override {
headphone_volume = volume;
set_wm_headphone_volume(volume);
}
volume_range_t headphone_gain_range() const override {
return {-121.0_dB, 6.0_dB};
}
@@ -328,11 +332,11 @@ class WM8731 : public audio::Codec {
}
void headphone_mute() {
set_headphone_volume(headphone_gain_range().min);
set_wm_headphone_volume(headphone_gain_range().min);
}
void headphone_enable() override {
set_headphone_volume(headphone_volume);
set_wm_headphone_volume(headphone_volume);
}
void headphone_disable() override {
@@ -60,3 +60,15 @@ TEST_CASE("Stash should save and restore value.") {
CHECK_EQ(val, 10);
}
TEST_CASE("ms_duration should return duration.") {
auto sample_rate = 48'000;
auto bytes_per_sample = 2; // 16 bit.
auto seconds = 5;
auto size = seconds * sample_rate * bytes_per_sample;
CHECK_EQ(ms_duration(size, sample_rate, bytes_per_sample), 5 * 1000);
}
TEST_CASE("ms_duration not fault when passed zero.") {
CHECK_EQ(ms_duration(0, 0, 0), 0);
}