mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-08-21 20:58:53 -04:00
Merge remote-tracking branch 'upstream/master'
Conflicts: firmware/application/Makefile firmware/application/core_control.cpp firmware/application/touch.cpp firmware/application/ui_debug.cpp firmware/application/ui_debug.hpp firmware/application/ui_navigation.cpp firmware/baseband/baseband_thread.cpp
This commit is contained in:
commit
1b0da68d65
252 changed files with 10752 additions and 7183 deletions
|
@ -127,6 +127,7 @@ CPPSRC = main.cpp \
|
|||
hackrf_hal.cpp \
|
||||
portapack.cpp \
|
||||
portapack_shared_memory.cpp \
|
||||
baseband_api.cpp \
|
||||
portapack_persistent_memory.cpp \
|
||||
portapack_io.cpp \
|
||||
i2c_pp.cpp \
|
||||
|
@ -146,6 +147,7 @@ CPPSRC = main.cpp \
|
|||
touch.cpp \
|
||||
touch_adc.cpp \
|
||||
encoder.cpp \
|
||||
audio.cpp \
|
||||
lcd_ili9341.cpp \
|
||||
ui.cpp \
|
||||
ui_alphanum.cpp \
|
||||
|
@ -167,6 +169,7 @@ CPPSRC = main.cpp \
|
|||
ui_debug.cpp \
|
||||
ui_baseband_stats_view.cpp \
|
||||
ui_sd_card_status_view.cpp \
|
||||
ui_sd_card_debug.cpp \
|
||||
ui_console.cpp \
|
||||
ui_receiver.cpp \
|
||||
ui_spectrum.cpp \
|
||||
|
@ -185,12 +188,15 @@ CPPSRC = main.cpp \
|
|||
../commom/ais_packet.cpp \
|
||||
ais_app.cpp \
|
||||
tpms_app.cpp \
|
||||
../common/tpms_packet.cpp \
|
||||
ert_app.cpp \
|
||||
../common/ert_packet.cpp \
|
||||
spectrum_analysis_app.cpp \
|
||||
capture_app.cpp \
|
||||
sd_card.cpp \
|
||||
file.cpp \
|
||||
log_file.cpp \
|
||||
png_writer.cpp \
|
||||
audio_thread.cpp \
|
||||
manchester.cpp \
|
||||
string_format.cpp \
|
||||
temperature_logger.cpp \
|
||||
|
@ -198,7 +204,8 @@ CPPSRC = main.cpp \
|
|||
../common/chibios_cpp.cpp \
|
||||
../common/debug.cpp \
|
||||
../common/gcc.cpp \
|
||||
m4_startup.cpp \
|
||||
../common/lfsr_random.cpp \
|
||||
core_control.cpp \
|
||||
cpld_max5.cpp \
|
||||
jtag.cpp \
|
||||
cpld_update.cpp \
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -42,26 +41,6 @@ static std::string latlon_abs_normalized(const int32_t normalized, const char su
|
|||
return to_string_dec_uint(degrees) + "." + to_string_dec_uint(fraction, 6, '0') + suffix;
|
||||
}
|
||||
|
||||
static std::string latitude(const Latitude value) {
|
||||
if( value.is_not_available() ) {
|
||||
return "not available";
|
||||
} else if( value.is_valid() ) {
|
||||
return latlon_abs_normalized(value.normalized(), "SN");
|
||||
} else {
|
||||
return "invalid";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string longitude(const Longitude value) {
|
||||
if( value.is_not_available() ) {
|
||||
return "not available";
|
||||
} else if( value.is_valid() ) {
|
||||
return latlon_abs_normalized(value.normalized(), "WE");
|
||||
} else {
|
||||
return "invalid";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string latlon(const Latitude latitude, const Longitude longitude) {
|
||||
if( latitude.is_valid() && longitude.is_valid() ) {
|
||||
return latlon_abs_normalized(latitude.normalized(), "SN") + " " + latlon_abs_normalized(longitude.normalized(), "WE");
|
||||
|
@ -78,17 +57,6 @@ static std::string mmsi(
|
|||
return to_string_dec_uint(mmsi, 9);
|
||||
}
|
||||
|
||||
static std::string datetime(
|
||||
const ais::DateTime& datetime
|
||||
) {
|
||||
return to_string_dec_uint(datetime.year, 4, '0') + "/" +
|
||||
to_string_dec_uint(datetime.month, 2, '0') + "/" +
|
||||
to_string_dec_uint(datetime.day, 2, '0') + " " +
|
||||
to_string_dec_uint(datetime.hour, 2, '0') + ":" +
|
||||
to_string_dec_uint(datetime.minute, 2, '0') + ":" +
|
||||
to_string_dec_uint(datetime.second, 2, '0');
|
||||
}
|
||||
|
||||
static std::string navigational_status(const unsigned int value) {
|
||||
switch(value) {
|
||||
case 0: return "under way w/engine";
|
||||
|
@ -160,6 +128,12 @@ static std::string true_heading(const TrueHeading value) {
|
|||
} /* namespace format */
|
||||
} /* namespace ais */
|
||||
|
||||
AISLogger::AISLogger(
|
||||
const std::string& file_path
|
||||
) : log_file { file_path }
|
||||
{
|
||||
}
|
||||
|
||||
void AISLogger::on_packet(const ais::Packet& packet) {
|
||||
// TODO: Unstuff here, not in baseband!
|
||||
if( log_file.is_ready() ) {
|
||||
|
@ -340,22 +314,28 @@ AISAppView::AISAppView(NavigationView&) {
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
target_frequency_ = initial_target_frequency;
|
||||
|
||||
radio::enable({
|
||||
tuning_frequency(),
|
||||
sampling_rate,
|
||||
baseband_bandwidth,
|
||||
rf::Direction::Receive,
|
||||
false, 32, 32,
|
||||
1,
|
||||
});
|
||||
|
||||
baseband::start({
|
||||
.mode = 3,
|
||||
.sampling_rate = sampling_rate,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
|
||||
options_channel.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
this->on_frequency_changed(v);
|
||||
};
|
||||
options_channel.set_by_value(162025000);
|
||||
|
||||
receiver_model.set_baseband_configuration({
|
||||
.mode = 3,
|
||||
.sampling_rate = 2457600,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
receiver_model.set_rf_amp(false);
|
||||
receiver_model.set_lna(32);
|
||||
receiver_model.set_vga(32);
|
||||
receiver_model.enable();
|
||||
options_channel.set_by_value(target_frequency());
|
||||
|
||||
recent_entries_view.on_select = [this](const AISRecentEntry& entry) {
|
||||
this->on_show_detail(entry);
|
||||
|
@ -363,10 +343,14 @@ AISAppView::AISAppView(NavigationView&) {
|
|||
recent_entry_detail_view.on_close = [this]() {
|
||||
this->on_show_list();
|
||||
};
|
||||
|
||||
logger = std::make_unique<AISLogger>("ais.txt");
|
||||
}
|
||||
|
||||
AISAppView::~AISAppView() {
|
||||
receiver_model.disable();
|
||||
baseband::stop();
|
||||
radio::disable();
|
||||
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::AISPacket);
|
||||
}
|
||||
|
||||
|
@ -382,7 +366,10 @@ void AISAppView::set_parent_rect(const Rect new_parent_rect) {
|
|||
}
|
||||
|
||||
void AISAppView::on_packet(const ais::Packet& packet) {
|
||||
logger.on_packet(packet);
|
||||
if( logger ) {
|
||||
logger->on_packet(packet);
|
||||
}
|
||||
|
||||
const auto updated_entry = recent.on_packet(packet.source_id(), packet);
|
||||
recent_entries_view.set_dirty();
|
||||
|
||||
|
@ -405,8 +392,21 @@ void AISAppView::on_show_detail(const AISRecentEntry& entry) {
|
|||
recent_entry_detail_view.focus();
|
||||
}
|
||||
|
||||
void AISAppView::on_frequency_changed(const uint32_t new_frequency) {
|
||||
receiver_model.set_tuning_frequency(new_frequency);
|
||||
void AISAppView::on_frequency_changed(const uint32_t new_target_frequency) {
|
||||
set_target_frequency(new_target_frequency);
|
||||
}
|
||||
|
||||
void AISAppView::set_target_frequency(const uint32_t new_value) {
|
||||
target_frequency_ = new_value;
|
||||
radio::set_tuning_frequency(tuning_frequency());
|
||||
}
|
||||
|
||||
uint32_t AISAppView::target_frequency() const {
|
||||
return target_frequency_;
|
||||
}
|
||||
|
||||
uint32_t AISAppView::tuning_frequency() const {
|
||||
return target_frequency() - (sampling_rate / 4);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
|
|
@ -90,10 +90,12 @@ using AISRecentEntries = RecentEntries<ais::Packet, AISRecentEntry>;
|
|||
|
||||
class AISLogger {
|
||||
public:
|
||||
AISLogger(const std::string& file_path);
|
||||
|
||||
void on_packet(const ais::Packet& packet);
|
||||
|
||||
private:
|
||||
LogFile log_file { "ais.txt" };
|
||||
LogFile log_file;
|
||||
};
|
||||
|
||||
namespace ui {
|
||||
|
@ -145,8 +147,12 @@ public:
|
|||
std::string title() const override { return "AIS"; };
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 162025000;
|
||||
static constexpr uint32_t sampling_rate = 2457600;
|
||||
static constexpr uint32_t baseband_bandwidth = 1750000;
|
||||
|
||||
AISRecentEntries recent;
|
||||
AISLogger logger;
|
||||
std::unique_ptr<AISLogger> logger;
|
||||
|
||||
AISRecentEntriesView recent_entries_view { recent };
|
||||
AISRecentEntryDetailView recent_entry_detail_view;
|
||||
|
@ -167,11 +173,18 @@ private:
|
|||
}
|
||||
};
|
||||
|
||||
uint32_t target_frequency_ = initial_target_frequency;
|
||||
|
||||
void on_packet(const ais::Packet& packet);
|
||||
void on_show_list();
|
||||
void on_show_detail(const AISRecentEntry& entry);
|
||||
|
||||
void on_frequency_changed(const uint32_t new_frequency);
|
||||
void on_frequency_changed(const uint32_t new_target_frequency);
|
||||
|
||||
uint32_t target_frequency() const;
|
||||
void set_target_frequency(const uint32_t new_value);
|
||||
|
||||
uint32_t tuning_frequency() const;
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
|
|
@ -22,9 +22,12 @@
|
|||
#include "analog_audio_app.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "audio.hpp"
|
||||
#include "file.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
@ -115,6 +118,9 @@ AnalogAudioView::AnalogAudioView(
|
|||
field_vga.on_change = [this](int32_t v_db) {
|
||||
this->on_vga_changed(v_db);
|
||||
};
|
||||
field_vga.on_show_options = [this]() {
|
||||
this->on_show_options_rf_gain();
|
||||
};
|
||||
|
||||
const auto modulation = receiver_model.modulation();
|
||||
options_modulation.set_by_value(modulation);
|
||||
|
@ -125,18 +131,20 @@ AnalogAudioView::AnalogAudioView(
|
|||
this->on_show_options_modulation();
|
||||
};
|
||||
|
||||
field_volume.set_value((receiver_model.headphone_volume() - wolfson::wm8731::headphone_gain_range.max).decibel() + 99);
|
||||
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
|
||||
field_volume.on_change = [this](int32_t v) {
|
||||
this->on_headphone_volume_changed(v);
|
||||
};
|
||||
|
||||
audio::output::start();
|
||||
|
||||
update_modulation(static_cast<ReceiverModel::Mode>(modulation));
|
||||
}
|
||||
|
||||
AnalogAudioView::~AnalogAudioView() {
|
||||
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
|
||||
// both?
|
||||
audio_codec.headphone_mute();
|
||||
audio::output::stop();
|
||||
|
||||
receiver_model.disable();
|
||||
}
|
||||
|
@ -212,16 +220,13 @@ void AnalogAudioView::on_show_options_frequency() {
|
|||
|
||||
field_frequency.set_style(&style_options_group);
|
||||
|
||||
auto widget = std::make_unique<FrequencyOptionsView>(
|
||||
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
|
||||
&style_options_group
|
||||
);
|
||||
auto widget = std::make_unique<FrequencyOptionsView>(options_view_rect, &style_options_group);
|
||||
|
||||
widget->set_step(receiver_model.frequency_step());
|
||||
widget->on_change_step = [this](rf::Frequency f) {
|
||||
this->on_frequency_step_changed(f);
|
||||
};
|
||||
widget->set_reference_ppm_correction(receiver_model.reference_ppm_correction());
|
||||
widget->set_reference_ppm_correction(persistent_memory::correction_ppb() / 1000);
|
||||
widget->on_change_reference_ppm_correction = [this](int32_t v) {
|
||||
this->on_reference_ppm_correction_changed(v);
|
||||
};
|
||||
|
@ -235,10 +240,7 @@ void AnalogAudioView::on_show_options_rf_gain() {
|
|||
|
||||
field_lna.set_style(&style_options_group);
|
||||
|
||||
auto widget = std::make_unique<RadioGainOptionsView>(
|
||||
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
|
||||
&style_options_group
|
||||
);
|
||||
auto widget = std::make_unique<RadioGainOptionsView>(options_view_rect, &style_options_group);
|
||||
|
||||
widget->set_rf_amp(receiver_model.rf_amp());
|
||||
widget->on_change_rf_amp = [this](bool enable) {
|
||||
|
@ -255,18 +257,12 @@ void AnalogAudioView::on_show_options_modulation() {
|
|||
const auto modulation = static_cast<ReceiverModel::Mode>(receiver_model.modulation());
|
||||
if( modulation == ReceiverModel::Mode::AMAudio ) {
|
||||
options_modulation.set_style(&style_options_group);
|
||||
auto widget = std::make_unique<AMOptionsView>(
|
||||
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
|
||||
&style_options_group
|
||||
);
|
||||
auto widget = std::make_unique<AMOptionsView>(options_view_rect, &style_options_group);
|
||||
set_options_widget(std::move(widget));
|
||||
}
|
||||
if( modulation == ReceiverModel::Mode::NarrowbandFMAudio ) {
|
||||
options_modulation.set_style(&style_options_group);
|
||||
auto widget = std::make_unique<NBFMOptionsView>(
|
||||
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
|
||||
&style_options_group
|
||||
);
|
||||
auto widget = std::make_unique<NBFMOptionsView>(options_view_rect, &style_options_group);
|
||||
set_options_widget(std::move(widget));
|
||||
}
|
||||
}
|
||||
|
@ -277,15 +273,18 @@ void AnalogAudioView::on_frequency_step_changed(rf::Frequency f) {
|
|||
}
|
||||
|
||||
void AnalogAudioView::on_reference_ppm_correction_changed(int32_t v) {
|
||||
receiver_model.set_reference_ppm_correction(v);
|
||||
persistent_memory::set_correction_ppb(v * 1000);
|
||||
}
|
||||
|
||||
void AnalogAudioView::on_headphone_volume_changed(int32_t v) {
|
||||
const auto new_volume = volume_t::decibel(v - 99) + wolfson::wm8731::headphone_gain_range.max;
|
||||
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
|
||||
receiver_model.set_headphone_volume(new_volume);
|
||||
}
|
||||
|
||||
void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
|
||||
audio::output::mute();
|
||||
audio_thread.reset();
|
||||
|
||||
const auto is_wideband_spectrum_mode = (modulation == ReceiverModel::Mode::SpectrumAnalysis);
|
||||
receiver_model.set_baseband_configuration({
|
||||
.mode = toUType(modulation),
|
||||
|
@ -294,6 +293,14 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
|
|||
});
|
||||
receiver_model.set_baseband_bandwidth(is_wideband_spectrum_mode ? 12000000 : 1750000);
|
||||
receiver_model.enable();
|
||||
|
||||
if( !is_wideband_spectrum_mode ) {
|
||||
const auto filename = next_filename_matching_pattern("AUD_????.S16");
|
||||
if( !filename.empty() ) {
|
||||
audio_thread = std::make_unique<AudioThread>(filename);
|
||||
}
|
||||
audio::output::unmute();
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
|
|
@ -27,6 +27,8 @@
|
|||
#include "ui_receiver.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
|
||||
#include "audio_thread.hpp"
|
||||
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
@ -93,6 +95,8 @@ public:
|
|||
private:
|
||||
static constexpr ui::Dim header_height = 2 * 16;
|
||||
|
||||
const Rect options_view_rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 };
|
||||
|
||||
RSSI rssi {
|
||||
{ 21 * 8, 0, 6 * 8, 4 },
|
||||
};
|
||||
|
@ -113,12 +117,8 @@ private:
|
|||
{ 15 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
NumberField field_vga {
|
||||
{ 18 * 8, 0 * 16},
|
||||
2,
|
||||
{ max2837::vga::gain_db_range.minimum, max2837::vga::gain_db_range.maximum },
|
||||
max2837::vga::gain_db_step,
|
||||
' ',
|
||||
VGAGainField field_vga {
|
||||
{ 18 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
OptionsField options_modulation {
|
||||
|
@ -144,6 +144,8 @@ private:
|
|||
|
||||
spectrum::WaterfallWidget waterfall;
|
||||
|
||||
std::unique_ptr<AudioThread> audio_thread;
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
|
||||
void on_rf_amp_changed(bool v);
|
||||
|
|
174
firmware/application/audio.cpp
Normal file
174
firmware/application/audio.cpp
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "audio.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using portapack::i2c0;
|
||||
using portapack::clock_manager;
|
||||
|
||||
#include "wm8731.hpp"
|
||||
using wolfson::wm8731::WM8731;
|
||||
|
||||
#include "i2s.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
namespace audio {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr i2s::ConfigTX i2s0_config_tx {
|
||||
.dao = i2s::DAO {
|
||||
.wordwidth = i2s::WordWidth::Bits16,
|
||||
.mono = 0,
|
||||
.stop = 1,
|
||||
.reset = 0,
|
||||
.ws_sel = 0,
|
||||
.ws_halfperiod = 0x0f,
|
||||
.mute = 1,
|
||||
},
|
||||
.txrate = i2s::MCLKRate {
|
||||
.x_divider = 0,
|
||||
.y_divider = 0,
|
||||
},
|
||||
.txbitrate = i2s::BitRate {
|
||||
.bitrate = 7,
|
||||
},
|
||||
.txmode = i2s::Mode {
|
||||
.clksel = i2s::ClockSelect::BaseAudioClkOrExternalMCLK,
|
||||
.four_pin = 0,
|
||||
.mclk_out_en = 1,
|
||||
},
|
||||
};
|
||||
|
||||
constexpr i2s::ConfigRX i2s0_config_rx {
|
||||
.dai = i2s::DAI {
|
||||
.wordwidth = i2s::WordWidth::Bits16,
|
||||
.mono = 0,
|
||||
.stop = 1,
|
||||
.reset = 0,
|
||||
.ws_sel = 1,
|
||||
.ws_halfperiod = 0x0f,
|
||||
},
|
||||
.rxrate = i2s::MCLKRate {
|
||||
.x_divider = 0,
|
||||
.y_divider = 0,
|
||||
},
|
||||
.rxbitrate = i2s::BitRate {
|
||||
.bitrate = 7,
|
||||
},
|
||||
.rxmode = i2s::Mode {
|
||||
.clksel = i2s::ClockSelect::BaseAudioClkOrExternalMCLK,
|
||||
.four_pin = 1,
|
||||
.mclk_out_en = 0,
|
||||
},
|
||||
};
|
||||
|
||||
constexpr i2s::ConfigDMA i2s0_config_dma {
|
||||
.dma1 = i2s::DMA {
|
||||
.rx_enable = 1,
|
||||
.tx_enable = 0,
|
||||
.rx_depth = 4,
|
||||
.tx_depth = 0,
|
||||
},
|
||||
.dma2 = i2s::DMA {
|
||||
.rx_enable = 0,
|
||||
.tx_enable = 1,
|
||||
.rx_depth = 0,
|
||||
.tx_depth = 4,
|
||||
},
|
||||
};
|
||||
|
||||
constexpr uint8_t wm8731_i2c_address = 0x1a;
|
||||
|
||||
WM8731 audio_codec { i2c0, wm8731_i2c_address };
|
||||
|
||||
} /* namespace */
|
||||
|
||||
namespace output {
|
||||
|
||||
void start() {
|
||||
i2s::i2s0::tx_start();
|
||||
unmute();
|
||||
}
|
||||
|
||||
void stop() {
|
||||
mute();
|
||||
i2s::i2s0::tx_stop();
|
||||
}
|
||||
|
||||
void mute() {
|
||||
i2s::i2s0::tx_mute();
|
||||
|
||||
audio_codec.headphone_mute();
|
||||
}
|
||||
|
||||
void unmute() {
|
||||
i2s::i2s0::tx_unmute();
|
||||
}
|
||||
|
||||
} /* namespace output */
|
||||
|
||||
namespace headphone {
|
||||
|
||||
volume_range_t volume_range() {
|
||||
return wolfson::wm8731::headphone_gain_range;
|
||||
}
|
||||
|
||||
void set_volume(const volume_t volume) {
|
||||
audio_codec.set_headphone_volume(volume);
|
||||
}
|
||||
|
||||
} /* namespace headphone */
|
||||
|
||||
namespace debug {
|
||||
|
||||
int reg_count() {
|
||||
return wolfson::wm8731::reg_count;
|
||||
}
|
||||
|
||||
uint16_t reg_read(const int register_number) {
|
||||
return audio_codec.read(register_number);
|
||||
}
|
||||
|
||||
} /* namespace debug */
|
||||
|
||||
void init() {
|
||||
clock_manager.start_audio_pll();
|
||||
audio_codec.init();
|
||||
|
||||
i2s::i2s0::configure(
|
||||
i2s0_config_tx,
|
||||
i2s0_config_rx,
|
||||
i2s0_config_dma
|
||||
);
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
audio_codec.reset();
|
||||
output::stop();
|
||||
}
|
||||
|
||||
void set_rate(const Rate rate) {
|
||||
clock_manager.set_base_audio_clock_divider(toUType(rate));
|
||||
}
|
||||
|
||||
} /* namespace audio */
|
69
firmware/application/audio.hpp
Normal file
69
firmware/application/audio.hpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __AUDIO_H__
|
||||
#define __AUDIO_H__
|
||||
|
||||
#include "volume.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace audio {
|
||||
|
||||
namespace output {
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
void mute();
|
||||
void unmute();
|
||||
|
||||
} /* namespace output */
|
||||
|
||||
namespace headphone {
|
||||
|
||||
volume_range_t volume_range();
|
||||
|
||||
void set_volume(const volume_t volume);
|
||||
|
||||
} /* namespace headphone */
|
||||
|
||||
namespace debug {
|
||||
|
||||
int reg_count();
|
||||
uint16_t reg_read(const int register_number);
|
||||
|
||||
} /* namespace debug */
|
||||
|
||||
void init();
|
||||
void shutdown();
|
||||
|
||||
enum class Rate {
|
||||
Hz_12000 = 4,
|
||||
Hz_24000 = 2,
|
||||
Hz_48000 = 1,
|
||||
};
|
||||
|
||||
void set_rate(const Rate rate);
|
||||
|
||||
} /* namespace audio */
|
||||
|
||||
#endif/*__AUDIO_H__*/
|
24
firmware/application/audio_thread.cpp
Normal file
24
firmware/application/audio_thread.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "audio_thread.hpp"
|
||||
|
||||
Thread* AudioThread::thread = nullptr;
|
145
firmware/application/audio_thread.hpp
Normal file
145
firmware/application/audio_thread.hpp
Normal file
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __AUDIO_THREAD_H__
|
||||
#define __AUDIO_THREAD_H__
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
#include "file.hpp"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "hackrf_gpio.hpp"
|
||||
using namespace hackrf::one;
|
||||
|
||||
#include <cstring>
|
||||
|
||||
class StreamOutput {
|
||||
public:
|
||||
StreamOutput(
|
||||
FIFO<uint8_t>* const fifo
|
||||
) : fifo { fifo }
|
||||
{
|
||||
}
|
||||
|
||||
size_t available() {
|
||||
return fifo->len();
|
||||
}
|
||||
|
||||
size_t read(void* const data, const size_t length) {
|
||||
return fifo->out(reinterpret_cast<uint8_t*>(data), length);
|
||||
}
|
||||
|
||||
private:
|
||||
FIFO<uint8_t>* const fifo;
|
||||
};
|
||||
|
||||
class AudioThread {
|
||||
public:
|
||||
AudioThread(
|
||||
std::string file_path
|
||||
) : file_path { std::move(file_path) },
|
||||
write_buffer { std::make_unique<std::array<uint8_t, write_size>>() }
|
||||
{
|
||||
// Need significant stack for FATFS
|
||||
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, AudioThread::static_fn, this);
|
||||
}
|
||||
|
||||
~AudioThread() {
|
||||
chThdTerminate(thread);
|
||||
chEvtSignal(thread, EVT_FIFO_HIGHWATER);
|
||||
const auto success = chThdWait(thread);
|
||||
|
||||
if( !success ) {
|
||||
led_tx.on();
|
||||
}
|
||||
}
|
||||
|
||||
static void check_fifo_isr() {
|
||||
if( (shared_memory.FIFO_HACK != nullptr) && (thread != nullptr) ) {
|
||||
auto fifo = reinterpret_cast<FIFO<uint8_t>*>(shared_memory.FIFO_HACK);
|
||||
if( fifo->len() >= write_size ) {
|
||||
chEvtSignalI(thread, EVT_FIFO_HIGHWATER);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t write_size = 16384;
|
||||
static constexpr eventmask_t EVT_FIFO_HIGHWATER = 1;
|
||||
|
||||
const std::string file_path;
|
||||
std::unique_ptr<std::array<uint8_t, write_size>> write_buffer;
|
||||
File file;
|
||||
static Thread* thread;
|
||||
|
||||
static msg_t static_fn(void* arg) {
|
||||
auto obj = static_cast<AudioThread*>(arg);
|
||||
return obj->run();
|
||||
}
|
||||
|
||||
msg_t run() {
|
||||
if( !file.open_for_writing(file_path) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fifo = reinterpret_cast<FIFO<uint8_t>*>(shared_memory.FIFO_HACK);
|
||||
if( !fifo ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StreamOutput stream { fifo };
|
||||
|
||||
while( !chThdShouldTerminate() ) {
|
||||
chEvtWaitAny(EVT_FIFO_HIGHWATER);
|
||||
|
||||
while( stream.available() >= write_buffer->size() ) {
|
||||
if( !transfer(stream, write_buffer.get()) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool transfer(StreamOutput& stream, std::array<uint8_t, write_size>* const write_buffer) {
|
||||
bool success = false;
|
||||
|
||||
led_usb.on();
|
||||
|
||||
const auto bytes_to_write = stream.read(write_buffer->data(), write_buffer->size());
|
||||
if( bytes_to_write == write_buffer->size() ) {
|
||||
if( file.write(write_buffer->data(), write_buffer->size()) ) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
led_usb.off();
|
||||
|
||||
return success;
|
||||
}
|
||||
};
|
||||
|
||||
#endif/*__AUDIO_THREAD_H__*/
|
105
firmware/application/baseband_api.cpp
Normal file
105
firmware/application/baseband_api.cpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "audio.hpp"
|
||||
#include "dsp_iir_config.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
namespace baseband {
|
||||
|
||||
void AMConfig::apply() const {
|
||||
const AMConfigureMessage message {
|
||||
taps_6k0_decim_0,
|
||||
taps_6k0_decim_1,
|
||||
taps_6k0_decim_2,
|
||||
channel,
|
||||
modulation,
|
||||
audio_12k_hpf_300hz_config
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
audio::set_rate(audio::Rate::Hz_12000);
|
||||
}
|
||||
|
||||
void NBFMConfig::apply() const {
|
||||
const NBFMConfigureMessage message {
|
||||
decim_0,
|
||||
decim_1,
|
||||
channel,
|
||||
2,
|
||||
deviation,
|
||||
audio_24k_hpf_300hz_config,
|
||||
audio_24k_deemph_300_6_config
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
}
|
||||
|
||||
void WFMConfig::apply() const {
|
||||
const WFMConfigureMessage message {
|
||||
taps_200k_wfm_decim_0,
|
||||
taps_200k_wfm_decim_1,
|
||||
taps_64_lp_156_198,
|
||||
75000,
|
||||
audio_48k_hpf_30hz_config,
|
||||
audio_48k_deemph_2122_6_config
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
audio::set_rate(audio::Rate::Hz_48000);
|
||||
}
|
||||
|
||||
void start(BasebandConfiguration configuration) {
|
||||
BasebandConfigurationMessage message { configuration };
|
||||
shared_memory.baseband_queue.push(message);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
shared_memory.baseband_queue.push_and_wait(
|
||||
BasebandConfigurationMessage {
|
||||
.configuration = { },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void shutdown() {
|
||||
ShutdownMessage shutdown_message;
|
||||
shared_memory.baseband_queue.push(shutdown_message);
|
||||
}
|
||||
|
||||
void spectrum_streaming_start() {
|
||||
shared_memory.baseband_queue.push_and_wait(
|
||||
SpectrumStreamingConfigMessage {
|
||||
SpectrumStreamingConfigMessage::Mode::Running
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void spectrum_streaming_stop() {
|
||||
shared_memory.baseband_queue.push_and_wait(
|
||||
SpectrumStreamingConfigMessage {
|
||||
SpectrumStreamingConfigMessage::Mode::Stopped
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
} /* namespace baseband */
|
63
firmware/application/baseband_api.hpp
Normal file
63
firmware/application/baseband_api.hpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __BASEBAND_API_H__
|
||||
#define __BASEBAND_API_H__
|
||||
|
||||
#include "message.hpp"
|
||||
|
||||
#include "dsp_fir_taps.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace baseband {
|
||||
|
||||
struct AMConfig {
|
||||
const fir_taps_complex<64> channel;
|
||||
const AMConfigureMessage::Modulation modulation;
|
||||
|
||||
void apply() const;
|
||||
};
|
||||
|
||||
struct NBFMConfig {
|
||||
const fir_taps_real<24> decim_0;
|
||||
const fir_taps_real<32> decim_1;
|
||||
const fir_taps_real<32> channel;
|
||||
const size_t deviation;
|
||||
|
||||
void apply() const;
|
||||
};
|
||||
|
||||
struct WFMConfig {
|
||||
void apply() const;
|
||||
};
|
||||
|
||||
void start(BasebandConfiguration configuration);
|
||||
void stop();
|
||||
|
||||
void shutdown();
|
||||
|
||||
void spectrum_streaming_start();
|
||||
void spectrum_streaming_stop();
|
||||
|
||||
} /* namespace baseband */
|
||||
|
||||
#endif/*__BASEBAND_API_H__*/
|
130
firmware/application/capture_app.cpp
Normal file
130
firmware/application/capture_app.cpp
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "capture_app.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "file.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
CaptureAppView::CaptureAppView(NavigationView& nav) {
|
||||
add_children({ {
|
||||
&button_start_stop,
|
||||
&rssi,
|
||||
&channel,
|
||||
&field_frequency,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&waterfall,
|
||||
} });
|
||||
|
||||
field_frequency.set_value(receiver_model.tuning_frequency());
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
// TODO: Provide separate modal method/scheme?
|
||||
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->on_tuning_frequency_changed(f);
|
||||
this->field_frequency.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
field_lna.set_value(receiver_model.lna());
|
||||
field_lna.on_change = [this](int32_t v) {
|
||||
this->on_lna_changed(v);
|
||||
};
|
||||
|
||||
field_vga.set_value(receiver_model.vga());
|
||||
field_vga.on_change = [this](int32_t v_db) {
|
||||
this->on_vga_changed(v_db);
|
||||
};
|
||||
|
||||
button_start_stop.on_select = [this](ImageButton&) {
|
||||
this->on_start_stop();
|
||||
};
|
||||
|
||||
receiver_model.set_baseband_configuration({
|
||||
.mode = toUType(ReceiverModel::Mode::Capture),
|
||||
.sampling_rate = sampling_rate,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
receiver_model.set_baseband_bandwidth(baseband_bandwidth);
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
CaptureAppView::~CaptureAppView() {
|
||||
receiver_model.disable();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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(), static_cast<ui::Dim>(new_parent_rect.height() - header_height) };
|
||||
waterfall.set_parent_rect(waterfall_rect);
|
||||
}
|
||||
|
||||
void CaptureAppView::focus() {
|
||||
button_start_stop.focus();
|
||||
}
|
||||
|
||||
void CaptureAppView::on_start_stop() {
|
||||
if( capture_thread ) {
|
||||
capture_thread.reset();
|
||||
button_start_stop.set_bitmap(&bitmap_record);
|
||||
} else {
|
||||
const auto filename = next_filename_matching_pattern("BBD_????.C16");
|
||||
if( filename.empty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
capture_thread = std::make_unique<AudioThread>(filename);
|
||||
button_start_stop.set_bitmap(&bitmap_stop);
|
||||
}
|
||||
}
|
||||
|
||||
void CaptureAppView::on_tuning_frequency_changed(rf::Frequency f) {
|
||||
receiver_model.set_tuning_frequency(f);
|
||||
}
|
||||
|
||||
void CaptureAppView::on_lna_changed(int32_t v_db) {
|
||||
receiver_model.set_lna(v_db);
|
||||
}
|
||||
|
||||
void CaptureAppView::on_vga_changed(int32_t v_db) {
|
||||
receiver_model.set_vga(v_db);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
142
firmware/application/capture_app.hpp
Normal file
142
firmware/application/capture_app.hpp
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __CAPTURE_APP_HPP__
|
||||
#define __CAPTURE_APP_HPP__
|
||||
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
|
||||
#include "audio_thread.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace ui {
|
||||
|
||||
static constexpr uint8_t bitmap_record_data[] = {
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0xc0, 0x03,
|
||||
0xf0, 0x0f,
|
||||
0xf8, 0x1f,
|
||||
0xf8, 0x1f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xf8, 0x1f,
|
||||
0xf8, 0x1f,
|
||||
0xf0, 0x0f,
|
||||
0xc0, 0x03,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
};
|
||||
|
||||
static constexpr Bitmap bitmap_record {
|
||||
{ 16, 16 }, bitmap_record_data
|
||||
};
|
||||
|
||||
static constexpr uint8_t bitmap_stop_data[] = {
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0xfc, 0x3f,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
};
|
||||
|
||||
static constexpr Bitmap bitmap_stop {
|
||||
{ 16, 16 }, bitmap_stop_data
|
||||
};
|
||||
|
||||
class CaptureAppView : public View {
|
||||
public:
|
||||
CaptureAppView(NavigationView& nav);
|
||||
~CaptureAppView();
|
||||
|
||||
void on_hide() override;
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Capture"; };
|
||||
|
||||
private:
|
||||
static constexpr ui::Dim header_height = 2 * 16;
|
||||
|
||||
static constexpr uint32_t sampling_rate = 4000000;
|
||||
static constexpr uint32_t baseband_bandwidth = 2500000;
|
||||
|
||||
std::unique_ptr<AudioThread> capture_thread;
|
||||
|
||||
void on_start_stop();
|
||||
|
||||
void on_tuning_frequency_changed(rf::Frequency f);
|
||||
void on_lna_changed(int32_t v_db);
|
||||
void on_vga_changed(int32_t v_db);
|
||||
|
||||
ImageButton button_start_stop {
|
||||
{ 0 * 8, 0, 2 * 8, 1 * 16 },
|
||||
&bitmap_record,
|
||||
Color::red(),
|
||||
Color::black()
|
||||
};
|
||||
|
||||
RSSI rssi {
|
||||
{ 21 * 8, 0, 6 * 8, 4 },
|
||||
};
|
||||
|
||||
Channel channel {
|
||||
{ 21 * 8, 5, 6 * 8, 4 },
|
||||
};
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 5 * 8, 0 * 16 },
|
||||
};
|
||||
|
||||
LNAGainField field_lna {
|
||||
{ 15 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
VGAGainField field_vga {
|
||||
{ 18 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
spectrum::WaterfallWidget waterfall;
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__CAPTURE_APP_HPP__*/
|
65
firmware/application/core_control.cpp
Normal file
65
firmware/application/core_control.cpp
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "core_control.hpp"
|
||||
|
||||
#include "hal.h"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
#include "message.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
char * modhash;
|
||||
|
||||
/* TODO: OK, this is cool, but how do I put the M4 to sleep so I can switch to
|
||||
* a different image? Other than asking the old image to sleep while the M0
|
||||
* makes changes?
|
||||
*
|
||||
* I suppose I could force M4MEMMAP to an invalid memory reason which would
|
||||
* cause an exception and effectively halt the M4. But that feels gross.
|
||||
*/
|
||||
void m4_init(const portapack::spi_flash::region_t from, const portapack::memory::region_t to) {
|
||||
/* Initialize M4 code RAM */
|
||||
std::memcpy(reinterpret_cast<void*>(to.base()), from.base(), from.size);
|
||||
|
||||
/* M4 core is assumed to be sleeping with interrupts off, so we can mess
|
||||
* with its address space and RAM without concern.
|
||||
*/
|
||||
LPC_CREG->M4MEMMAP = to.base();
|
||||
|
||||
/* Reset M4 core */
|
||||
LPC_RGU->RESET_CTRL[0] = (1 << 13);
|
||||
}
|
||||
|
||||
void m4_request_shutdown() {
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void m0_halt() {
|
||||
rgu::reset(rgu::Reset::M0APP);
|
||||
while(true) {
|
||||
port_wait_for_interrupt();
|
||||
}
|
||||
}
|
|
@ -19,8 +19,8 @@
|
|||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __M4_STARTUP_H__
|
||||
#define __M4_STARTUP_H__
|
||||
#ifndef __CORE_CONTROL_H__
|
||||
#define __CORE_CONTROL_H__
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
|
@ -34,4 +34,6 @@ void m4_request_shutdown();
|
|||
void m4_switch(const char * hash);
|
||||
int m4_load_image(void);
|
||||
|
||||
#endif/*__M4_STARTUP_H__*/
|
||||
void m0_halt();
|
||||
|
||||
#endif/*__CORE_CONTROL_H__*/
|
|
@ -23,8 +23,7 @@
|
|||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "manchester.hpp"
|
||||
|
||||
|
@ -52,10 +51,20 @@ std::string consumption(Consumption value) {
|
|||
return to_string_dec_uint(value, 10);
|
||||
}
|
||||
|
||||
std::string commodity_type(CommodityType value) {
|
||||
return to_string_dec_uint(value, 2);
|
||||
}
|
||||
|
||||
} /* namespace format */
|
||||
|
||||
} /* namespace ert */
|
||||
|
||||
ERTLogger::ERTLogger(
|
||||
const std::string& file_path
|
||||
) : log_file { file_path }
|
||||
{
|
||||
}
|
||||
|
||||
void ERTLogger::on_packet(const ert::Packet& packet) {
|
||||
if( log_file.is_ready() ) {
|
||||
const auto formatted = packet.symbols_formatted();
|
||||
|
@ -63,6 +72,8 @@ void ERTLogger::on_packet(const ert::Packet& packet) {
|
|||
}
|
||||
}
|
||||
|
||||
const ERTRecentEntry::Key ERTRecentEntry::invalid_key { };
|
||||
|
||||
void ERTRecentEntry::update(const ert::Packet& packet) {
|
||||
received_count++;
|
||||
|
||||
|
@ -71,8 +82,9 @@ void ERTRecentEntry::update(const ert::Packet& packet) {
|
|||
|
||||
namespace ui {
|
||||
|
||||
static const std::array<std::pair<std::string, size_t>, 3> ert_columns { {
|
||||
static const std::array<std::pair<std::string, size_t>, 4> ert_columns { {
|
||||
{ "ID", 10 },
|
||||
{ "Tp", 2 },
|
||||
{ "Consumpt", 10 },
|
||||
{ "Cnt", 3 },
|
||||
} };
|
||||
|
@ -106,7 +118,7 @@ void RecentEntriesView<ERTRecentEntries>::draw(
|
|||
) {
|
||||
const auto& draw_style = is_selected ? style.invert() : style;
|
||||
|
||||
std::string line = ert::format::id(entry.id) + " " + ert::format::consumption(entry.last_consumption);
|
||||
std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption);
|
||||
|
||||
if( entry.received_count > 999 ) {
|
||||
line += " +++";
|
||||
|
@ -131,21 +143,28 @@ ERTAppView::ERTAppView(NavigationView&) {
|
|||
}
|
||||
);
|
||||
|
||||
receiver_model.set_baseband_configuration({
|
||||
radio::enable({
|
||||
initial_target_frequency,
|
||||
sampling_rate,
|
||||
baseband_bandwidth,
|
||||
rf::Direction::Receive,
|
||||
false, 32, 32,
|
||||
1,
|
||||
});
|
||||
|
||||
baseband::start({
|
||||
.mode = 6,
|
||||
.sampling_rate = 4194304,
|
||||
.sampling_rate = sampling_rate,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
receiver_model.set_baseband_bandwidth(2500000);
|
||||
receiver_model.set_rf_amp(false);
|
||||
receiver_model.set_lna(32);
|
||||
receiver_model.set_vga(32);
|
||||
receiver_model.set_tuning_frequency(911600000);
|
||||
receiver_model.enable();
|
||||
|
||||
logger = std::make_unique<ERTLogger>("ert.txt");
|
||||
}
|
||||
|
||||
ERTAppView::~ERTAppView() {
|
||||
receiver_model.disable();
|
||||
baseband::stop();
|
||||
radio::disable();
|
||||
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::ERTPacket);
|
||||
}
|
||||
|
||||
|
@ -159,10 +178,12 @@ void ERTAppView::set_parent_rect(const Rect new_parent_rect) {
|
|||
}
|
||||
|
||||
void ERTAppView::on_packet(const ert::Packet& packet) {
|
||||
logger.on_packet(packet);
|
||||
if( logger ) {
|
||||
logger->on_packet(packet);
|
||||
}
|
||||
|
||||
if( packet.crc_ok() ) {
|
||||
recent.on_packet(packet.id(), packet);
|
||||
recent.on_packet({ packet.id(), packet.commodity_type() }, packet);
|
||||
recent_entries_view.set_dirty();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,13 +33,37 @@
|
|||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
struct ERTKey {
|
||||
ert::ID id;
|
||||
ert::CommodityType commodity_type;
|
||||
|
||||
constexpr ERTKey(
|
||||
ert::ID id = ert::invalid_id,
|
||||
ert::CommodityType commodity_type = ert::invalid_commodity_type
|
||||
) : id { id },
|
||||
commodity_type { commodity_type }
|
||||
{
|
||||
}
|
||||
|
||||
ERTKey& operator=(const ERTKey& other) {
|
||||
id = other.id;
|
||||
commodity_type = other.commodity_type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const ERTKey& other) const {
|
||||
return (id == other.id) && (commodity_type == other.commodity_type);
|
||||
}
|
||||
};
|
||||
|
||||
struct ERTRecentEntry {
|
||||
using Key = ert::ID;
|
||||
using Key = ERTKey;
|
||||
|
||||
// TODO: Is this the right choice of invalid key value?
|
||||
static constexpr Key invalid_key = 0;
|
||||
static const Key invalid_key;
|
||||
|
||||
ert::ID id { invalid_key };
|
||||
ert::ID id { ert::invalid_id };
|
||||
ert::CommodityType commodity_type { ert::invalid_commodity_type };
|
||||
|
||||
size_t received_count { 0 };
|
||||
|
||||
|
@ -47,12 +71,13 @@ struct ERTRecentEntry {
|
|||
|
||||
ERTRecentEntry(
|
||||
const Key& key
|
||||
) : id { key }
|
||||
) : id { key.id },
|
||||
commodity_type { key.commodity_type }
|
||||
{
|
||||
}
|
||||
|
||||
Key key() const {
|
||||
return id;
|
||||
return { id, commodity_type };
|
||||
}
|
||||
|
||||
void update(const ert::Packet& packet);
|
||||
|
@ -60,10 +85,12 @@ struct ERTRecentEntry {
|
|||
|
||||
class ERTLogger {
|
||||
public:
|
||||
ERTLogger(const std::string& file_path);
|
||||
|
||||
void on_packet(const ert::Packet& packet);
|
||||
|
||||
private:
|
||||
LogFile log_file { "ert.txt" };
|
||||
LogFile log_file;
|
||||
};
|
||||
|
||||
using ERTRecentEntries = RecentEntries<ert::Packet, ERTRecentEntry>;
|
||||
|
@ -74,6 +101,10 @@ using ERTRecentEntriesView = RecentEntriesView<ERTRecentEntries>;
|
|||
|
||||
class ERTAppView : public View {
|
||||
public:
|
||||
static constexpr uint32_t initial_target_frequency = 911600000;
|
||||
static constexpr uint32_t sampling_rate = 4194304;
|
||||
static constexpr uint32_t baseband_bandwidth = 2500000;
|
||||
|
||||
ERTAppView(NavigationView& nav);
|
||||
~ERTAppView();
|
||||
|
||||
|
@ -89,7 +120,7 @@ public:
|
|||
|
||||
private:
|
||||
ERTRecentEntries recent;
|
||||
ERTLogger logger;
|
||||
std::unique_ptr<ERTLogger> logger;
|
||||
|
||||
ERTRecentEntriesView recent_entries_view { recent };
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
|
||||
#include "irq_controls.hpp"
|
||||
|
||||
#include "audio_thread.hpp"
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
|
@ -44,6 +46,7 @@ CH_IRQ_HANDLER(M4Core_IRQHandler) {
|
|||
CH_IRQ_PROLOGUE();
|
||||
|
||||
chSysLockFromIsr();
|
||||
AudioThread::check_fifo_isr();
|
||||
EventDispatcher::events_flag_isr(EVT_MASK_APPLICATION);
|
||||
chSysUnlockFromIsr();
|
||||
|
||||
|
@ -56,6 +59,7 @@ CH_IRQ_HANDLER(M4Core_IRQHandler) {
|
|||
|
||||
MessageHandlerMap EventDispatcher::message_map_;
|
||||
Thread* EventDispatcher::thread_event_loop = nullptr;
|
||||
Thread* EventDispatcher::thread_record = nullptr;
|
||||
|
||||
EventDispatcher::EventDispatcher(
|
||||
ui::Widget* const top_widget,
|
||||
|
@ -65,6 +69,8 @@ EventDispatcher::EventDispatcher(
|
|||
painter(painter),
|
||||
context(context)
|
||||
{
|
||||
init_message_queues();
|
||||
|
||||
thread_event_loop = chThdSelf();
|
||||
touch_manager.on_event = [this](const ui::TouchEvent event) {
|
||||
this->on_touch_event(event);
|
||||
|
@ -132,10 +138,9 @@ void EventDispatcher::dispatch(const eventmask_t events) {
|
|||
}
|
||||
|
||||
void EventDispatcher::handle_application_queue() {
|
||||
std::array<uint8_t, Message::MAX_SIZE> message_buffer;
|
||||
while(Message* const message = shared_memory.application_queue.pop(message_buffer)) {
|
||||
shared_memory.application_queue.handle([](Message* const message) {
|
||||
message_map().send(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void EventDispatcher::handle_rtc_tick() {
|
||||
|
@ -241,4 +246,15 @@ void EventDispatcher::event_bubble_encoder(const ui::EncoderEvent event) {
|
|||
while( (target != nullptr) && !target->on_encoder(event) ) {
|
||||
target = target->parent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::init_message_queues() {
|
||||
new (&shared_memory.baseband_queue) MessageQueue(
|
||||
shared_memory.baseband_queue_data, SharedMemory::baseband_queue_k
|
||||
);
|
||||
new (&shared_memory.application_queue) MessageQueue(
|
||||
shared_memory.application_queue_data, SharedMemory::application_queue_k
|
||||
);
|
||||
|
||||
shared_memory.FIFO_HACK = nullptr;
|
||||
}
|
||||
|
|
|
@ -73,6 +73,8 @@ public:
|
|||
return message_map_;
|
||||
}
|
||||
|
||||
static Thread* thread_record;
|
||||
|
||||
private:
|
||||
static MessageHandlerMap message_map_;
|
||||
static Thread* thread_event_loop;
|
||||
|
@ -105,6 +107,8 @@ private:
|
|||
|
||||
bool event_bubble_key(const ui::KeyEvent event);
|
||||
void event_bubble_encoder(const ui::EncoderEvent event);
|
||||
|
||||
void init_message_queues();
|
||||
};
|
||||
|
||||
#endif/*__EVENT_M0_H__*/
|
||||
|
|
|
@ -1,273 +1,279 @@
|
|||
/* CHIBIOS FIX */
|
||||
#include "ch.h"
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module configuration file R0.10c (C)ChaN, 2014
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FFCONF 80376 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Functions and Buffer Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS
|
||||
/ bytes. Instead of private sector buffer eliminated from the file object,
|
||||
/ common sector buffer in the file system object (FATFS) is used for the file
|
||||
/ data transfer. */
|
||||
|
||||
|
||||
#define _FS_READONLY 0
|
||||
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||
/ Read-only configuration removes basic writing API functions, f_write(),
|
||||
/ f_sync(), f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(),
|
||||
/ f_getfree() and optional writing functions as well. */
|
||||
|
||||
|
||||
#define _FS_MINIMIZE 0
|
||||
/* This option defines minimization level to remove some API functions.
|
||||
/
|
||||
/ 0: All basic functions are enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
|
||||
/ f_truncate() and f_rename() function are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 3: f_lseek() function is removed in addition to 2. */
|
||||
|
||||
|
||||
#define _USE_STRFUNC 1
|
||||
/* This option switches string functions, f_gets(), f_putc(), f_puts() and
|
||||
/ f_printf().
|
||||
/
|
||||
/ 0: Disable string functions.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion. */
|
||||
|
||||
|
||||
#define _USE_MKFS 0
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable)
|
||||
/ To enable it, also _FS_READONLY need to be set to 0. */
|
||||
|
||||
|
||||
#define _USE_FASTSEEK 0
|
||||
/* This option switches fast seek feature. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
|
||||
/* To enable it, also _FS_TINY need to be set to 1. */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _CODE_PAGE 1252
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect setting of the code page can cause a file open failure.
|
||||
/
|
||||
/ 932 - Japanese Shift_JIS (DBCS, OEM, Windows)
|
||||
/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows)
|
||||
/ 949 - Korean (DBCS, OEM, Windows)
|
||||
/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows)
|
||||
/ 1250 - Central Europe (Windows)
|
||||
/ 1251 - Cyrillic (Windows)
|
||||
/ 1252 - Latin 1 (Windows)
|
||||
/ 1253 - Greek (Windows)
|
||||
/ 1254 - Turkish (Windows)
|
||||
/ 1255 - Hebrew (Windows)
|
||||
/ 1256 - Arabic (Windows)
|
||||
/ 1257 - Baltic (Windows)
|
||||
/ 1258 - Vietnam (OEM, Windows)
|
||||
/ 437 - U.S. (OEM)
|
||||
/ 720 - Arabic (OEM)
|
||||
/ 737 - Greek (OEM)
|
||||
/ 775 - Baltic (OEM)
|
||||
/ 850 - Multilingual Latin 1 (OEM)
|
||||
/ 858 - Multilingual Latin 1 + Euro (OEM)
|
||||
/ 852 - Latin 2 (OEM)
|
||||
/ 855 - Cyrillic (OEM)
|
||||
/ 866 - Russian (OEM)
|
||||
/ 857 - Turkish (OEM)
|
||||
/ 862 - Hebrew (OEM)
|
||||
/ 874 - Thai (OEM, Windows)
|
||||
/ 1 - ASCII (No extended character. Valid for only non-LFN configuration.) */
|
||||
|
||||
|
||||
#define _USE_LFN 0
|
||||
#define _MAX_LFN 255
|
||||
/* The _USE_LFN option switches the LFN feature.
|
||||
/
|
||||
/ 0: Disable LFN feature. _MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ When enable the LFN feature, Unicode handling functions (option/unicode.c) must
|
||||
/ be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
/ ff_memfree(), must be added to the project. */
|
||||
|
||||
|
||||
#define _LFN_UNICODE 0
|
||||
/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode)
|
||||
/ To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE
|
||||
/ to 1. This option also affects behavior of string I/O functions. */
|
||||
|
||||
|
||||
#define _STRF_ENCODE 3
|
||||
/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to
|
||||
/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
|
||||
/
|
||||
/ 0: ANSI/OEM
|
||||
/ 1: UTF-16LE
|
||||
/ 2: UTF-16BE
|
||||
/ 3: UTF-8
|
||||
/
|
||||
/ When _LFN_UNICODE is 0, this option has no effect. */
|
||||
|
||||
|
||||
#define _FS_RPATH 0
|
||||
/* This option configures relative path feature.
|
||||
/
|
||||
/ 0: Disable relative path feature and remove related functions.
|
||||
/ 1: Enable relative path feature. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
/
|
||||
/ Note that directory items read via f_readdir() are affected by this option. */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 1
|
||||
/* Number of volumes (logical drives) to be used. */
|
||||
|
||||
|
||||
#define _STR_VOLUME_ID 0
|
||||
#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
|
||||
/* _STR_VOLUME_ID option switches string volume ID feature.
|
||||
/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
|
||||
/ number in the path name. _VOLUME_STRS defines the drive ID strings for each
|
||||
/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for
|
||||
/ the drive ID strings are: A-Z and 0-9. */
|
||||
|
||||
|
||||
#define _MULTI_PARTITION 0
|
||||
/* This option switches multi-partition feature. By default (0), each logical drive
|
||||
/ number is bound to the same physical drive number and only an FAT volume found on
|
||||
/ the physical drive will be mounted. When multi-partition feature is enabled (1),
|
||||
/ each logical drive number is bound to arbitrary physical drive and partition
|
||||
/ listed in the VolToPart[]. Also f_fdisk() funciton will be enabled. */
|
||||
|
||||
|
||||
#define _MIN_SS 512
|
||||
#define _MAX_SS 512
|
||||
/* These options configure the range of sector size to be supported. (512, 1024,
|
||||
/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
|
||||
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _USE_TRIM 0
|
||||
/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable)
|
||||
/ To enable Trim feature, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _FS_NOFSINFO 0
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_NORTC 0
|
||||
#define _NORTC_MON 11
|
||||
#define _NORTC_MDAY 9
|
||||
#define _NORTC_YEAR 2014
|
||||
/* The _FS_NORTC option switches timestamp feature. If the system does not have
|
||||
/ an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable
|
||||
/ the timestamp feature. All objects modified by FatFs will have a fixed timestamp
|
||||
/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR.
|
||||
/ When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need
|
||||
/ to be added to the project to read current time form RTC. _NORTC_MON,
|
||||
/ _NORTC_MDAY and _NORTC_YEAR have no effect.
|
||||
/ These options have no effect at read-only configuration (_FS_READONLY == 1). */
|
||||
|
||||
|
||||
#define _FS_LOCK 0
|
||||
/* The _FS_LOCK option switches file lock feature to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when _FS_READONLY
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock feature. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock feature. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ lock feature is independent of re-entrancy. */
|
||||
|
||||
|
||||
#define _FS_REENTRANT 1
|
||||
#define _FS_TIMEOUT 1000
|
||||
#define _SYNC_t Semaphore *
|
||||
/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs
|
||||
/ module itself. Note that regardless of this option, file access to different
|
||||
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||
/ to the same volume is under control of this feature.
|
||||
/
|
||||
/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||
/ function, must be added to the project. Samples are available in
|
||||
/ option/syscall.c.
|
||||
/
|
||||
/ The _FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
|
||||
/ SemaphoreHandle_t and etc.. */
|
||||
|
||||
|
||||
#define _WORD_ACCESS 0
|
||||
/* The _WORD_ACCESS option is an only platform dependent option. It defines
|
||||
/ which access method is used to the word data on the FAT volume.
|
||||
/
|
||||
/ 0: Byte-by-byte access. Always compatible with all platforms.
|
||||
/ 1: Word access. Do not choose this unless under both the following conditions.
|
||||
/
|
||||
/ * Address misaligned memory access is always allowed to ALL instructions.
|
||||
/ * Byte order on the memory is little-endian.
|
||||
/
|
||||
/ If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size.
|
||||
/ Following table shows allowable settings of some processor types.
|
||||
/
|
||||
/ ARM7TDMI 0 ColdFire 0 V850E 0
|
||||
/ Cortex-M3 0 Z80 0/1 V850ES 0/1
|
||||
/ Cortex-M0 0 x86 0/1 TLCS-870 0/1
|
||||
/ AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1
|
||||
/ AVR32 0 RL78 0 R32C 0
|
||||
/ PIC18 0/1 SH-2 0 M16C 0/1
|
||||
/ PIC24 0 H8S 0 MSP430 0
|
||||
/ PIC32 0 H8/300H 0 8051 0/1
|
||||
*/
|
||||
|
||||
/* CHIBIOS FIX */
|
||||
#include "ch.h"
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ FatFs - FAT file system module configuration file R0.11a (C)ChaN, 2015
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FFCONF 64180 /* Revision ID */
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Function Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_READONLY 0
|
||||
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
|
||||
/ Read-only configuration removes writing API functions, f_write(), f_sync(),
|
||||
/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
|
||||
/ and optional writing functions as well. */
|
||||
|
||||
|
||||
#define _FS_MINIMIZE 0
|
||||
/* This option defines minimization level to remove some basic API functions.
|
||||
/
|
||||
/ 0: All basic functions are enabled.
|
||||
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
|
||||
/ f_truncate() and f_rename() function are removed.
|
||||
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
|
||||
/ 3: f_lseek() function is removed in addition to 2. */
|
||||
|
||||
|
||||
#define _USE_STRFUNC 1
|
||||
/* This option switches string functions, f_gets(), f_putc(), f_puts() and
|
||||
/ f_printf().
|
||||
/
|
||||
/ 0: Disable string functions.
|
||||
/ 1: Enable without LF-CRLF conversion.
|
||||
/ 2: Enable with LF-CRLF conversion. */
|
||||
|
||||
|
||||
#define _USE_FIND 1
|
||||
/* This option switches filtered directory read feature and related functions,
|
||||
/ f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_MKFS 0
|
||||
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FASTSEEK 1
|
||||
/* This option switches fast seek feature. (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_LABEL 0
|
||||
/* This option switches volume label functions, f_getlabel() and f_setlabel().
|
||||
/ (0:Disable or 1:Enable) */
|
||||
|
||||
|
||||
#define _USE_FORWARD 0
|
||||
/* This option switches f_forward() function. (0:Disable or 1:Enable)
|
||||
/ To enable it, also _FS_TINY need to be set to 1. */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Locale and Namespace Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _CODE_PAGE 437
|
||||
/* This option specifies the OEM code page to be used on the target system.
|
||||
/ Incorrect setting of the code page can cause a file open failure.
|
||||
/
|
||||
/ 1 - ASCII (No extended character. Non-LFN cfg. only)
|
||||
/ 437 - U.S.
|
||||
/ 720 - Arabic
|
||||
/ 737 - Greek
|
||||
/ 771 - KBL
|
||||
/ 775 - Baltic
|
||||
/ 850 - Latin 1
|
||||
/ 852 - Latin 2
|
||||
/ 855 - Cyrillic
|
||||
/ 857 - Turkish
|
||||
/ 860 - Portuguese
|
||||
/ 861 - Icelandic
|
||||
/ 862 - Hebrew
|
||||
/ 863 - Canadian French
|
||||
/ 864 - Arabic
|
||||
/ 865 - Nordic
|
||||
/ 866 - Russian
|
||||
/ 869 - Greek 2
|
||||
/ 932 - Japanese (DBCS)
|
||||
/ 936 - Simplified Chinese (DBCS)
|
||||
/ 949 - Korean (DBCS)
|
||||
/ 950 - Traditional Chinese (DBCS)
|
||||
*/
|
||||
|
||||
|
||||
#define _USE_LFN 0
|
||||
#define _MAX_LFN 255
|
||||
/* The _USE_LFN option switches the LFN feature.
|
||||
/
|
||||
/ 0: Disable LFN feature. _MAX_LFN has no effect.
|
||||
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
|
||||
/ 2: Enable LFN with dynamic working buffer on the STACK.
|
||||
/ 3: Enable LFN with dynamic working buffer on the HEAP.
|
||||
/
|
||||
/ When enable the LFN feature, Unicode handling functions (option/unicode.c) must
|
||||
/ be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes.
|
||||
/ When use stack for the working buffer, take care on stack overflow. When use heap
|
||||
/ memory for the working buffer, memory management functions, ff_memalloc() and
|
||||
/ ff_memfree(), must be added to the project. */
|
||||
|
||||
|
||||
#define _LFN_UNICODE 0
|
||||
/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode)
|
||||
/ To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE
|
||||
/ to 1. This option also affects behavior of string I/O functions. */
|
||||
|
||||
|
||||
#define _STRF_ENCODE 3
|
||||
/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to
|
||||
/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
|
||||
/
|
||||
/ 0: ANSI/OEM
|
||||
/ 1: UTF-16LE
|
||||
/ 2: UTF-16BE
|
||||
/ 3: UTF-8
|
||||
/
|
||||
/ When _LFN_UNICODE is 0, this option has no effect. */
|
||||
|
||||
|
||||
#define _FS_RPATH 0
|
||||
/* This option configures relative path feature.
|
||||
/
|
||||
/ 0: Disable relative path feature and remove related functions.
|
||||
/ 1: Enable relative path feature. f_chdir() and f_chdrive() are available.
|
||||
/ 2: f_getcwd() function is available in addition to 1.
|
||||
/
|
||||
/ Note that directory items read via f_readdir() are affected by this option. */
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ Drive/Volume Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 1
|
||||
/* Number of volumes (logical drives) to be used. */
|
||||
|
||||
|
||||
#define _STR_VOLUME_ID 0
|
||||
#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
|
||||
/* _STR_VOLUME_ID option switches string volume ID feature.
|
||||
/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
|
||||
/ number in the path name. _VOLUME_STRS defines the drive ID strings for each
|
||||
/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for
|
||||
/ the drive ID strings are: A-Z and 0-9. */
|
||||
|
||||
|
||||
#define _MULTI_PARTITION 0
|
||||
/* This option switches multi-partition feature. By default (0), each logical drive
|
||||
/ number is bound to the same physical drive number and only an FAT volume found on
|
||||
/ the physical drive will be mounted. When multi-partition feature is enabled (1),
|
||||
/ each logical drive number is bound to arbitrary physical drive and partition
|
||||
/ listed in the VolToPart[]. Also f_fdisk() funciton will be available. */
|
||||
|
||||
|
||||
#define _MIN_SS 512
|
||||
#define _MAX_SS 512
|
||||
/* These options configure the range of sector size to be supported. (512, 1024,
|
||||
/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
|
||||
/ harddisk. But a larger value may be required for on-board flash memory and some
|
||||
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
|
||||
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _USE_TRIM 0
|
||||
/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable)
|
||||
/ To enable Trim feature, also CTRL_TRIM command should be implemented to the
|
||||
/ disk_ioctl() function. */
|
||||
|
||||
|
||||
#define _FS_NOFSINFO 0
|
||||
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
|
||||
/ option, and f_getfree() function at first time after volume mount will force
|
||||
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
|
||||
/
|
||||
/ bit0=0: Use free cluster count in the FSINFO if available.
|
||||
/ bit0=1: Do not trust free cluster count in the FSINFO.
|
||||
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
|
||||
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*---------------------------------------------------------------------------/
|
||||
/ System Configurations
|
||||
/---------------------------------------------------------------------------*/
|
||||
|
||||
#define _FS_TINY 0
|
||||
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
|
||||
/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS
|
||||
/ bytes. Instead of private sector buffer eliminated from the file object,
|
||||
/ common sector buffer in the file system object (FATFS) is used for the file
|
||||
/ data transfer. */
|
||||
|
||||
|
||||
#define _FS_NORTC 0
|
||||
#define _NORTC_MON 1
|
||||
#define _NORTC_MDAY 1
|
||||
#define _NORTC_YEAR 2015
|
||||
/* The _FS_NORTC option switches timestamp feature. If the system does not have
|
||||
/ an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable
|
||||
/ the timestamp feature. All objects modified by FatFs will have a fixed timestamp
|
||||
/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR.
|
||||
/ When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need
|
||||
/ to be added to the project to read current time form RTC. _NORTC_MON,
|
||||
/ _NORTC_MDAY and _NORTC_YEAR have no effect.
|
||||
/ These options have no effect at read-only configuration (_FS_READONLY == 1). */
|
||||
|
||||
|
||||
#define _FS_LOCK 0
|
||||
/* The _FS_LOCK option switches file lock feature to control duplicated file open
|
||||
/ and illegal operation to open objects. This option must be 0 when _FS_READONLY
|
||||
/ is 1.
|
||||
/
|
||||
/ 0: Disable file lock feature. To avoid volume corruption, application program
|
||||
/ should avoid illegal open, remove and rename to the open objects.
|
||||
/ >0: Enable file lock feature. The value defines how many files/sub-directories
|
||||
/ can be opened simultaneously under file lock control. Note that the file
|
||||
/ lock feature is independent of re-entrancy. */
|
||||
|
||||
|
||||
#define _FS_REENTRANT 1
|
||||
#define _FS_TIMEOUT 1000
|
||||
#define _SYNC_t Semaphore *
|
||||
/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs
|
||||
/ module itself. Note that regardless of this option, file access to different
|
||||
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
|
||||
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
|
||||
/ to the same volume is under control of this feature.
|
||||
/
|
||||
/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
|
||||
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
|
||||
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
|
||||
/ function, must be added to the project. Samples are available in
|
||||
/ option/syscall.c.
|
||||
/
|
||||
/ The _FS_TIMEOUT defines timeout period in unit of time tick.
|
||||
/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
|
||||
/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be
|
||||
/ included somewhere in the scope of ff.c. */
|
||||
|
||||
|
||||
#define _WORD_ACCESS 0
|
||||
/* The _WORD_ACCESS option is an only platform dependent option. It defines
|
||||
/ which access method is used to the word data on the FAT volume.
|
||||
/
|
||||
/ 0: Byte-by-byte access. Always compatible with all platforms.
|
||||
/ 1: Word access. Do not choose this unless under both the following conditions.
|
||||
/
|
||||
/ * Address misaligned memory access is always allowed to ALL instructions.
|
||||
/ * Byte order on the memory is little-endian.
|
||||
/
|
||||
/ If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size.
|
||||
/ Following table shows allowable settings of some type of processors.
|
||||
/
|
||||
/ ARM7TDMI 0 *2 ColdFire 0 *1 V850E 0 *2
|
||||
/ Cortex-M3 0 *3 Z80 0/1 V850ES 0/1
|
||||
/ Cortex-M0 0 *2 x86 0/1 TLCS-870 0/1
|
||||
/ AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1
|
||||
/ AVR32 0 *1 RL78 0 *2 R32C 0 *2
|
||||
/ PIC18 0/1 SH-2 0 *1 M16C 0/1
|
||||
/ PIC24 0 *2 H8S 0 *1 MSP430 0 *2
|
||||
/ PIC32 0 *1 H8/300H 0 *1 8051 0/1
|
||||
/
|
||||
/ *1:Big-endian.
|
||||
/ *2:Unaligned memory access is not supported.
|
||||
/ *3:Some compilers generate LDM/STM for mem_cpy function.
|
||||
*/
|
||||
|
||||
|
|
|
@ -21,13 +21,24 @@
|
|||
|
||||
#include "file.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
File::~File() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool File::open_for_append(const std::string file_path) {
|
||||
bool File::open_for_writing(const std::string& file_path) {
|
||||
const auto open_result = f_open(&f, file_path.c_str(), FA_WRITE | FA_OPEN_ALWAYS);
|
||||
if( open_result == FR_OK ) {
|
||||
return (open_result == FR_OK);
|
||||
}
|
||||
|
||||
bool File::open_for_reading(const std::string& file_path) {
|
||||
const auto open_result = f_open(&f, file_path.c_str(), FA_READ | FA_OPEN_EXISTING);
|
||||
return (open_result == FR_OK);
|
||||
}
|
||||
|
||||
bool File::open_for_append(const std::string& file_path) {
|
||||
if( open_for_writing(file_path) ) {
|
||||
const auto seek_result = f_lseek(&f, f_size(&f));
|
||||
if( seek_result == FR_OK ) {
|
||||
return true;
|
||||
|
@ -60,7 +71,7 @@ bool File::write(const void* const data, const size_t bytes_to_write) {
|
|||
return (result == FR_OK) && (bytes_written == bytes_to_write);
|
||||
}
|
||||
|
||||
bool File::puts(const std::string string) {
|
||||
bool File::puts(const std::string& string) {
|
||||
const auto result = f_puts(string.c_str(), &f);
|
||||
return (result >= 0);
|
||||
}
|
||||
|
@ -69,3 +80,91 @@ bool File::sync() {
|
|||
const auto result = f_sync(&f);
|
||||
return (result == FR_OK);
|
||||
}
|
||||
|
||||
static std::string find_last_file_matching_pattern(const std::string& pattern) {
|
||||
std::string last_match;
|
||||
for(const auto& entry : std::filesystem::directory_iterator("", pattern.c_str())) {
|
||||
if( std::filesystem::is_regular_file(entry.status()) ) {
|
||||
const auto match = entry.path();
|
||||
if( match > last_match ) {
|
||||
last_match = match;
|
||||
}
|
||||
}
|
||||
}
|
||||
return last_match;
|
||||
}
|
||||
|
||||
static std::string increment_filename_ordinal(const std::string& filename) {
|
||||
std::string result { filename };
|
||||
|
||||
auto it = result.rbegin();
|
||||
|
||||
// Back up past extension.
|
||||
for(; it != result.rend(); ++it) {
|
||||
if( *it == '.' ) {
|
||||
++it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( it == result.rend() ) {
|
||||
return { };
|
||||
}
|
||||
|
||||
// Increment decimal number before the extension.
|
||||
for(; it != result.rend(); ++it) {
|
||||
const auto c = *it;
|
||||
if( c < '0' ) {
|
||||
return { };
|
||||
} else if( c < '9' ) {
|
||||
*it += 1;
|
||||
break;
|
||||
} else if( c == '9' ) {
|
||||
*it = '0';
|
||||
} else {
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string next_filename_matching_pattern(const std::string& filename_pattern) {
|
||||
auto filename = find_last_file_matching_pattern(filename_pattern);
|
||||
if( filename.empty() ) {
|
||||
filename = filename_pattern;
|
||||
std::replace(std::begin(filename), std::end(filename), '?', '0');
|
||||
} else {
|
||||
filename = increment_filename_ordinal(filename);
|
||||
}
|
||||
return filename;
|
||||
}
|
||||
|
||||
namespace std {
|
||||
namespace filesystem {
|
||||
|
||||
directory_iterator::directory_iterator(
|
||||
const char* path,
|
||||
const char* wild
|
||||
) {
|
||||
impl = std::make_shared<Impl>();
|
||||
const auto result = f_findfirst(&impl->dir, &impl->filinfo, path, wild);
|
||||
if( result != FR_OK ) {
|
||||
impl.reset();
|
||||
// TODO: Throw exception if/when I enable exceptions...
|
||||
}
|
||||
}
|
||||
|
||||
directory_iterator& directory_iterator::operator++() {
|
||||
const auto result = f_findnext(&impl->dir, &impl->filinfo);
|
||||
if( (result != FR_OK) || (impl->filinfo.fname[0] == 0) ) {
|
||||
impl.reset();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool is_regular_file(const file_status s) {
|
||||
return !(s & AM_DIR);
|
||||
}
|
||||
|
||||
} /* namespace filesystem */
|
||||
} /* namespace std */
|
||||
|
|
|
@ -26,12 +26,17 @@
|
|||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <iterator>
|
||||
|
||||
class File {
|
||||
public:
|
||||
~File();
|
||||
|
||||
bool open_for_append(const std::string file_path);
|
||||
bool open_for_writing(const std::string& file_path);
|
||||
bool open_for_reading(const std::string& file_path);
|
||||
bool open_for_append(const std::string& file_path);
|
||||
bool close();
|
||||
|
||||
bool is_ready();
|
||||
|
@ -39,14 +44,76 @@ public:
|
|||
bool read(void* const data, const size_t bytes_to_read);
|
||||
bool write(const void* const data, const size_t bytes_to_write);
|
||||
|
||||
bool puts(const std::string string);
|
||||
template<size_t N>
|
||||
bool write(const std::array<uint8_t, N>& data) {
|
||||
return write(data.data(), N);
|
||||
}
|
||||
|
||||
bool puts(const std::string& string);
|
||||
|
||||
bool sync();
|
||||
|
||||
private:
|
||||
const std::string file_path;
|
||||
|
||||
FIL f;
|
||||
};
|
||||
|
||||
std::string next_filename_matching_pattern(const std::string& filename_pattern);
|
||||
|
||||
namespace std {
|
||||
namespace filesystem {
|
||||
|
||||
using file_status = BYTE;
|
||||
|
||||
struct directory_entry : public FILINFO {
|
||||
file_status status() const {
|
||||
return fattrib;
|
||||
}
|
||||
|
||||
const std::string path() const noexcept { return fname; };
|
||||
};
|
||||
|
||||
class directory_iterator {
|
||||
struct Impl {
|
||||
DIR dir;
|
||||
directory_entry filinfo;
|
||||
|
||||
~Impl() {
|
||||
f_closedir(&dir);
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<Impl> impl;
|
||||
|
||||
friend bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs);
|
||||
|
||||
public:
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = directory_entry;
|
||||
using pointer = const directory_entry*;
|
||||
using reference = const directory_entry&;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
directory_iterator() noexcept { };
|
||||
directory_iterator(const char* path, const char* wild);
|
||||
|
||||
~directory_iterator() { }
|
||||
|
||||
directory_iterator& operator++();
|
||||
|
||||
reference operator*() const {
|
||||
// TODO: Exception or assert if impl == nullptr.
|
||||
return impl->filinfo;
|
||||
}
|
||||
};
|
||||
|
||||
inline const directory_iterator& begin(const directory_iterator& iter) noexcept { return iter; };
|
||||
inline directory_iterator end(const directory_iterator&) noexcept { return { }; };
|
||||
|
||||
inline bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs) { return lhs.impl != rhs.impl; };
|
||||
|
||||
bool is_regular_file(const file_status s);
|
||||
|
||||
} /* namespace filesystem */
|
||||
} /* namespace std */
|
||||
|
||||
#endif/*__FILE_H__*/
|
||||
|
|
|
@ -261,7 +261,7 @@
|
|||
* lower priority, this may slow down the driver a bit however.
|
||||
*/
|
||||
#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
|
||||
#define SDC_NICE_WAITING TRUE
|
||||
#define SDC_NICE_WAITING FALSE
|
||||
#endif
|
||||
|
||||
/*===========================================================================*/
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
using namespace lpc43xx;
|
||||
|
||||
LogFile::LogFile(
|
||||
const std::string file_path
|
||||
const std::string& file_path
|
||||
) : file_path { file_path }
|
||||
{
|
||||
file.open_for_append(file_path);
|
||||
|
|
|
@ -32,7 +32,7 @@ using namespace lpc43xx;
|
|||
|
||||
class LogFile {
|
||||
public:
|
||||
LogFile(const std::string file_path);
|
||||
LogFile(const std::string& file_path);
|
||||
~LogFile();
|
||||
|
||||
bool is_ready();
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "m4_startup.hpp"
|
||||
|
||||
#include "hal.h"
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
#include "message.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
char * modhash;
|
||||
|
||||
/* TODO: OK, this is cool, but how do I put the M4 to sleep so I can switch to
|
||||
* a different image? Other than asking the old image to sleep while the M0
|
||||
* makes changes?
|
||||
*
|
||||
* I suppose I could force M4MEMMAP to an invalid memory reason which would
|
||||
* cause an exception and effectively halt the M4. But that feels gross.
|
||||
*/
|
||||
void m4_init(const portapack::spi_flash::region_t from, const portapack::memory::region_t to) {
|
||||
/* Initialize M4 code RAM */
|
||||
// DEBUG
|
||||
std::memcpy(reinterpret_cast<void*>(to.base()), from.base(), from.size);
|
||||
|
||||
/* M4 core is assumed to be sleeping with interrupts off, so we can mess
|
||||
* with its address space and RAM without concern.
|
||||
*/
|
||||
LPC_CREG->M4MEMMAP = to.base();
|
||||
|
||||
/* Reset M4 core */
|
||||
LPC_RGU->RESET_CTRL[0] = (1 << 13);
|
||||
}
|
||||
|
||||
int m4_load_image(void) {
|
||||
const char magic[6] = {'P', 'P', 'M', ' ', 0x01, 0x00};
|
||||
//uint32_t mod_size;
|
||||
UINT bw;
|
||||
uint8_t i;
|
||||
uint16_t cnt;
|
||||
char md5sum[16];
|
||||
FILINFO modinfo;
|
||||
FIL modfile;
|
||||
DIR rootdir;
|
||||
FRESULT res;
|
||||
|
||||
// Scan SD card root directory for files with the right MD5 fingerprint at the right location
|
||||
f_opendir(&rootdir, "/");
|
||||
for (;;) {
|
||||
res = f_readdir(&rootdir, &modinfo);
|
||||
if (res != FR_OK || modinfo.fname[0] == 0) break; // Reached last file, abort
|
||||
// Only care about files with .bin extension
|
||||
if ((!(modinfo.fattrib & AM_DIR)) && (modinfo.fname[9] == 'B') && (modinfo.fname[10] == 'I') && (modinfo.fname[11] == 'N')) {
|
||||
f_open(&modfile, modinfo.fname, FA_OPEN_EXISTING | FA_READ);
|
||||
// Magic bytes and version check
|
||||
f_read(&modfile, &md5sum, 6, &bw);
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (md5sum[i] != magic[i]) break;
|
||||
}
|
||||
if (i == 6) {
|
||||
f_lseek(&modfile, 26);
|
||||
f_read(&modfile, &md5sum, 16, &bw);
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (md5sum[i] != modhash[i]) break;
|
||||
}
|
||||
if (i == 16) {
|
||||
//f_lseek(&modfile, 6);
|
||||
//f_read(&modfile, &mod_size, 4, &bw);
|
||||
f_lseek(&modfile, 256);
|
||||
// For some reason, f_read > 512 bytes at once crashes everything... :/
|
||||
for (cnt=0;cnt<256;cnt++)
|
||||
f_read(&modfile, reinterpret_cast<void*>(portapack::memory::map::m4_code.base()+(cnt*256)), 256, &bw);
|
||||
f_close(&modfile);
|
||||
LPC_RGU->RESET_CTRL[0] = (1 << 13);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
f_close(&modfile);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void m4_switch(const char * hash) {
|
||||
modhash = const_cast<char*>(hash);
|
||||
|
||||
// Ask M4 to enter wait loop in RAM
|
||||
/*BasebandConfiguration baseband_switch {
|
||||
.mode = 255,
|
||||
.sampling_rate = 0,
|
||||
.decimation_factor = 1,
|
||||
};
|
||||
|
||||
BasebandConfigurationMessage message { baseband_switch };
|
||||
shared_memory.baseband_queue.push(message);*/
|
||||
}
|
||||
|
||||
void m4_request_shutdown() {
|
||||
ShutdownMessage shutdown_message;
|
||||
shared_memory.baseband_queue.push(shutdown_message);
|
||||
}
|
|
@ -68,7 +68,7 @@
|
|||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "m4_startup.hpp"
|
||||
#include "core_control.hpp"
|
||||
#include "spi_image.hpp"
|
||||
|
||||
#include "debug.hpp"
|
||||
|
@ -76,9 +76,6 @@
|
|||
|
||||
#include "gcc.hpp"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
#include "sd_card.hpp"
|
||||
|
||||
#include <string.h>
|
||||
|
@ -95,8 +92,6 @@ int main(void) {
|
|||
|
||||
sdcStart(&SDCD1, nullptr);
|
||||
|
||||
init_message_queues();
|
||||
|
||||
ui::Context context;
|
||||
ui::SystemView system_view {
|
||||
context,
|
||||
|
@ -129,8 +124,7 @@ int main(void) {
|
|||
|
||||
portapack::shutdown();
|
||||
m4_init(portapack::spi_flash::hackrf, portapack::memory::map::m4_code_hackrf);
|
||||
|
||||
rgu::reset(rgu::Reset::M0APP);
|
||||
m0_halt();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -29,9 +29,8 @@ using namespace hackrf::one;
|
|||
|
||||
#include "clock_manager.hpp"
|
||||
|
||||
#include "i2c_pp.hpp"
|
||||
|
||||
#include "touch_adc.hpp"
|
||||
#include "audio.hpp"
|
||||
|
||||
namespace portapack {
|
||||
|
||||
|
@ -51,8 +50,6 @@ I2C i2c0(&I2CD0);
|
|||
SPI ssp0(&SPID1);
|
||||
SPI ssp1(&SPID2);
|
||||
|
||||
wolfson::wm8731::WM8731 audio_codec { i2c0, portapack::wm8731_i2c_address };
|
||||
|
||||
si5351::Si5351 clock_generator {
|
||||
i2c0, hackrf::one::si5351_i2c_address
|
||||
};
|
||||
|
@ -134,9 +131,8 @@ void init() {
|
|||
clock_manager.set_reference_ppb(persistent_memory::correction_ppb());
|
||||
clock_manager.run_at_full_speed();
|
||||
|
||||
clock_manager.start_audio_pll();
|
||||
audio_codec.init();
|
||||
|
||||
audio::init();
|
||||
|
||||
clock_manager.enable_first_if_clock();
|
||||
clock_manager.enable_second_if_clock();
|
||||
clock_manager.enable_codec_clocks();
|
||||
|
@ -149,7 +145,7 @@ void shutdown() {
|
|||
display.shutdown();
|
||||
|
||||
radio::disable();
|
||||
audio_codec.reset();
|
||||
audio::shutdown();
|
||||
clock_manager.shutdown();
|
||||
|
||||
power.shutdown();
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
#include "receiver_model.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
|
||||
#include "i2c_pp.hpp"
|
||||
#include "spi_pp.hpp"
|
||||
#include "wm8731.hpp"
|
||||
#include "si5351.hpp"
|
||||
#include "lcd_ili9341.hpp"
|
||||
|
||||
|
@ -39,11 +39,10 @@ extern portapack::IO io;
|
|||
|
||||
extern lcd::ILI9341 display;
|
||||
|
||||
extern I2C i2c0;
|
||||
extern SPI ssp0;
|
||||
extern SPI ssp1;
|
||||
|
||||
extern wolfson::wm8731::WM8731 audio_codec;
|
||||
|
||||
extern si5351::Si5351 clock_generator;
|
||||
extern ClockManager clock_manager;
|
||||
|
||||
|
|
|
@ -22,9 +22,11 @@
|
|||
#include "radio.hpp"
|
||||
|
||||
#include "rf_path.hpp"
|
||||
|
||||
#include "rffc507x.hpp"
|
||||
#include "max2837.hpp"
|
||||
#include "max5864.hpp"
|
||||
#include "baseband_cpld.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "tuning.hpp"
|
||||
|
||||
|
@ -148,6 +150,10 @@ void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum) {
|
|||
second_if.set_lpf_rf_bandwidth(bandwidth_minimum);
|
||||
}
|
||||
|
||||
void set_baseband_rate(const uint32_t rate) {
|
||||
portapack::clock_manager.set_sampling_frequency(rate);
|
||||
}
|
||||
|
||||
void set_baseband_decimation_by(const size_t n) {
|
||||
baseband_cpld.set_decimation_by(n);
|
||||
}
|
||||
|
@ -165,4 +171,43 @@ void disable() {
|
|||
set_rf_amp(false);
|
||||
}
|
||||
|
||||
void enable(Configuration configuration) {
|
||||
configure(configuration);
|
||||
}
|
||||
|
||||
void configure(Configuration configuration) {
|
||||
set_tuning_frequency(configuration.tuning_frequency);
|
||||
set_rf_amp(configuration.rf_amp);
|
||||
set_lna_gain(configuration.lna_gain);
|
||||
set_vga_gain(configuration.vga_gain);
|
||||
set_baseband_rate(configuration.baseband_rate);
|
||||
set_baseband_decimation_by(configuration.baseband_decimation);
|
||||
set_baseband_filter_bandwidth(configuration.baseband_filter_bandwidth);
|
||||
set_direction(configuration.direction);
|
||||
}
|
||||
|
||||
namespace debug {
|
||||
|
||||
namespace first_if {
|
||||
|
||||
uint32_t register_read(const size_t register_number) {
|
||||
return radio::first_if.read(register_number);
|
||||
}
|
||||
|
||||
} /* namespace first_if */
|
||||
|
||||
namespace second_if {
|
||||
|
||||
uint32_t register_read(const size_t register_number) {
|
||||
return radio::second_if.read(register_number);
|
||||
}
|
||||
|
||||
uint8_t temp_sense() {
|
||||
return radio::second_if.temp_sense() & 0x1f;
|
||||
}
|
||||
|
||||
} /* namespace second_if */
|
||||
|
||||
} /* namespace debug */
|
||||
|
||||
} /* namespace radio */
|
||||
|
|
|
@ -27,11 +27,19 @@
|
|||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include "rffc507x.hpp"
|
||||
#include "max2837.hpp"
|
||||
|
||||
namespace radio {
|
||||
|
||||
struct Configuration {
|
||||
rf::Frequency tuning_frequency;
|
||||
uint32_t baseband_rate;
|
||||
uint32_t baseband_filter_bandwidth;
|
||||
rf::Direction direction;
|
||||
bool rf_amp;
|
||||
int8_t lna_gain;
|
||||
int8_t vga_gain;
|
||||
uint8_t baseband_decimation;
|
||||
};
|
||||
|
||||
void init();
|
||||
|
||||
void set_direction(const rf::Direction new_direction);
|
||||
|
@ -39,15 +47,33 @@ bool set_tuning_frequency(const rf::Frequency frequency);
|
|||
void set_rf_amp(const bool rf_amp);
|
||||
void set_lna_gain(const int_fast8_t db);
|
||||
void set_vga_gain(const int_fast8_t db);
|
||||
void set_sampling_frequency(const uint32_t frequency);
|
||||
void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum);
|
||||
void set_baseband_rate(const uint32_t rate);
|
||||
void set_baseband_decimation_by(const size_t n);
|
||||
void set_antenna_bias(const bool on);
|
||||
|
||||
void enable(Configuration configuration);
|
||||
void configure(Configuration configuration);
|
||||
void disable();
|
||||
|
||||
extern rffc507x::RFFC507x first_if;
|
||||
extern max2837::MAX2837 second_if;
|
||||
namespace debug {
|
||||
|
||||
namespace first_if {
|
||||
|
||||
uint32_t register_read(const size_t register_number);
|
||||
|
||||
} /* namespace first_if */
|
||||
|
||||
namespace second_if {
|
||||
|
||||
uint32_t register_read(const size_t register_number);
|
||||
|
||||
// TODO: This belongs somewhere else.
|
||||
uint8_t temp_sense();
|
||||
|
||||
} /* namespace second_if */
|
||||
|
||||
} /* namespace debug */
|
||||
|
||||
} /* namespace radio */
|
||||
|
||||
|
|
|
@ -21,90 +21,33 @@
|
|||
|
||||
#include "receiver_model.hpp"
|
||||
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "radio.hpp"
|
||||
#include "audio.hpp"
|
||||
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
#include "dsp_iir_config.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
struct AMConfig {
|
||||
const fir_taps_complex<64> channel;
|
||||
const AMConfigureMessage::Modulation modulation;
|
||||
|
||||
void apply() const;
|
||||
};
|
||||
|
||||
struct NBFMConfig {
|
||||
const fir_taps_real<24> decim_0;
|
||||
const fir_taps_real<32> decim_1;
|
||||
const fir_taps_real<32> channel;
|
||||
const size_t deviation;
|
||||
|
||||
void apply() const;
|
||||
};
|
||||
|
||||
struct WFMConfig {
|
||||
void apply() const;
|
||||
};
|
||||
|
||||
void AMConfig::apply() const {
|
||||
const AMConfigureMessage message {
|
||||
taps_6k0_decim_0,
|
||||
taps_6k0_decim_1,
|
||||
taps_6k0_decim_2,
|
||||
channel,
|
||||
modulation,
|
||||
audio_12k_hpf_300hz_config
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
clock_manager.set_base_audio_clock_divider(4);
|
||||
}
|
||||
|
||||
void NBFMConfig::apply() const {
|
||||
const NBFMConfigureMessage message {
|
||||
decim_0,
|
||||
decim_1,
|
||||
channel,
|
||||
2,
|
||||
deviation,
|
||||
audio_24k_hpf_300hz_config,
|
||||
audio_24k_deemph_300_6_config
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
clock_manager.set_base_audio_clock_divider(2);
|
||||
}
|
||||
|
||||
void WFMConfig::apply() const {
|
||||
const WFMConfigureMessage message {
|
||||
taps_200k_wfm_decim_0,
|
||||
taps_200k_wfm_decim_1,
|
||||
taps_64_lp_156_198,
|
||||
75000,
|
||||
audio_48k_hpf_30hz_config,
|
||||
audio_48k_deemph_2122_6_config
|
||||
};
|
||||
shared_memory.baseband_queue.push(message);
|
||||
clock_manager.set_base_audio_clock_divider(1);
|
||||
}
|
||||
|
||||
static constexpr std::array<AMConfig, 3> am_configs { {
|
||||
static constexpr std::array<baseband::AMConfig, 3> am_configs { {
|
||||
{ taps_6k0_dsb_channel, AMConfigureMessage::Modulation::DSB },
|
||||
{ taps_2k8_usb_channel, AMConfigureMessage::Modulation::SSB },
|
||||
{ taps_2k8_lsb_channel, AMConfigureMessage::Modulation::SSB },
|
||||
} };
|
||||
|
||||
static constexpr std::array<NBFMConfig, 3> nbfm_configs { {
|
||||
static constexpr std::array<baseband::NBFMConfig, 3> nbfm_configs { {
|
||||
{ taps_4k25_decim_0, taps_4k25_decim_1, taps_4k25_channel, 2500 },
|
||||
{ taps_11k0_decim_0, taps_11k0_decim_1, taps_11k0_channel, 2500 },
|
||||
{ taps_16k0_decim_0, taps_16k0_decim_1, taps_16k0_channel, 5000 },
|
||||
} };
|
||||
|
||||
static constexpr std::array<WFMConfig, 1> wfm_configs { {
|
||||
static constexpr std::array<baseband::WFMConfig, 1> wfm_configs { {
|
||||
{ },
|
||||
} };
|
||||
|
||||
|
@ -127,15 +70,6 @@ void ReceiverModel::set_frequency_step(rf::Frequency f) {
|
|||
frequency_step_ = f;
|
||||
}
|
||||
|
||||
int32_t ReceiverModel::reference_ppm_correction() const {
|
||||
return persistent_memory::correction_ppb() / 1000;
|
||||
}
|
||||
|
||||
void ReceiverModel::set_reference_ppm_correction(int32_t v) {
|
||||
persistent_memory::set_correction_ppb(v * 1000);
|
||||
clock_manager.set_reference_ppb(v * 1000);
|
||||
}
|
||||
|
||||
bool ReceiverModel::antenna_bias() const {
|
||||
return antenna_bias_;
|
||||
}
|
||||
|
@ -217,18 +151,10 @@ void ReceiverModel::enable() {
|
|||
update_headphone_volume();
|
||||
}
|
||||
|
||||
void ReceiverModel::baseband_disable() {
|
||||
shared_memory.baseband_queue.push_and_wait(
|
||||
BasebandConfigurationMessage {
|
||||
.configuration = { },
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void ReceiverModel::disable() {
|
||||
enabled_ = false;
|
||||
update_antenna_bias();
|
||||
baseband_disable();
|
||||
baseband::stop();
|
||||
|
||||
// TODO: Responsibility for enabling/disabling the radio is muddy.
|
||||
// Some happens in ReceiverModel, some inside radio namespace.
|
||||
|
@ -236,8 +162,7 @@ void ReceiverModel::disable() {
|
|||
}
|
||||
|
||||
int32_t ReceiverModel::tuning_offset() {
|
||||
if( (baseband_configuration.mode == 4) ||
|
||||
(baseband_configuration.mode == 6) ) {
|
||||
if( (baseband_configuration.mode == 4) ) {
|
||||
return 0;
|
||||
} else {
|
||||
return -(sampling_rate() / 4);
|
||||
|
@ -300,21 +225,20 @@ void ReceiverModel::update_baseband_configuration() {
|
|||
// protocols that need quick RX/TX turn-around.
|
||||
|
||||
// Disabling baseband while changing sampling rates seems like a good idea...
|
||||
baseband_disable();
|
||||
baseband::stop();
|
||||
|
||||
clock_manager.set_sampling_frequency(sampling_rate() * baseband_oversampling());
|
||||
radio::set_baseband_rate(sampling_rate() * baseband_oversampling());
|
||||
update_tuning_frequency();
|
||||
radio::set_baseband_decimation_by(baseband_oversampling());
|
||||
|
||||
BasebandConfigurationMessage message { baseband_configuration };
|
||||
shared_memory.baseband_queue.push(message);
|
||||
baseband::start(baseband_configuration);
|
||||
}
|
||||
|
||||
void ReceiverModel::update_headphone_volume() {
|
||||
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
|
||||
// both?
|
||||
|
||||
audio_codec.set_headphone_volume(headphone_volume_);
|
||||
audio::headphone::set_volume(headphone_volume_);
|
||||
}
|
||||
|
||||
void ReceiverModel::update_modulation_configuration() {
|
||||
|
|
|
@ -36,10 +36,8 @@ public:
|
|||
AMAudio = 0,
|
||||
NarrowbandFMAudio = 1,
|
||||
WidebandFMAudio = 2,
|
||||
AIS = 3,
|
||||
SpectrumAnalysis = 4,
|
||||
TPMS = 5,
|
||||
ERT = 6,
|
||||
Capture = 7,
|
||||
};
|
||||
|
||||
rf::Frequency tuning_frequency() const;
|
||||
|
@ -48,9 +46,6 @@ public:
|
|||
rf::Frequency frequency_step() const;
|
||||
void set_frequency_step(rf::Frequency f);
|
||||
|
||||
int32_t reference_ppm_correction() const;
|
||||
void set_reference_ppm_correction(int32_t v);
|
||||
|
||||
bool antenna_bias() const;
|
||||
void set_antenna_bias(bool enabled);
|
||||
|
||||
|
@ -122,8 +117,6 @@ private:
|
|||
void update_am_configuration();
|
||||
void update_nbfm_configuration();
|
||||
void update_wfm_configuration();
|
||||
|
||||
void baseband_disable();
|
||||
};
|
||||
|
||||
#endif/*__RECEIVER_MODEL_H__*/
|
||||
|
|
|
@ -129,7 +129,7 @@ public:
|
|||
Entries& recent
|
||||
) : recent { recent }
|
||||
{
|
||||
flags.focusable = true;
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
void paint(Painter& painter) override {
|
||||
|
|
|
@ -278,21 +278,4 @@ spi::reg_t RFFC507x::readback(const Readback readback) {
|
|||
return read(Register::READBACK);
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Test of RFFC507x reset over temperature */
|
||||
while(true) {
|
||||
first_if.write(rffc507x::Register::P1_FREQ2, 0xAAAA);
|
||||
first_if.reset();
|
||||
const auto after_reset = first_if.read(rffc507x::Register::P1_FREQ2);
|
||||
if( after_reset != 0x6276 ) {
|
||||
led_usb.off();
|
||||
led_tx.on();
|
||||
chThdSleepMilliseconds(100);
|
||||
} else {
|
||||
led_usb.on();
|
||||
led_tx.off();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
} /* namespace rffc507x */
|
||||
|
|
|
@ -39,16 +39,12 @@ FRESULT mount() {
|
|||
return f_mount(&fs, "", 0);
|
||||
}
|
||||
|
||||
FRESULT unmount() {
|
||||
return f_mount(NULL, "", 0);
|
||||
}
|
||||
|
||||
} /* namespace */
|
||||
|
||||
Signal<Status> status_signal;
|
||||
|
||||
void poll_inserted() {
|
||||
const auto card_present_now = sdc_lld_is_card_inserted(&SDCD1);
|
||||
const auto card_present_now = sdcIsCardInserted(&SDCD1);
|
||||
if( card_present_now != card_present ) {
|
||||
card_present = card_present_now;
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ std::vector<TemperatureLogger::sample_t> TemperatureLogger::history() const {
|
|||
|
||||
TemperatureLogger::sample_t TemperatureLogger::read_sample() {
|
||||
// MAX2837 does not return a valid temperature if in "shutdown" mode.
|
||||
return radio::second_if.temp_sense() & 0x1f;
|
||||
return radio::debug::second_if::temp_sense();
|
||||
}
|
||||
|
||||
void TemperatureLogger::push_sample(const TemperatureLogger::sample_t sample) {
|
||||
|
|
|
@ -22,9 +22,6 @@
|
|||
#include "touch.hpp"
|
||||
|
||||
namespace touch {
|
||||
|
||||
float Manager::cmx;
|
||||
float Manager::cmy;
|
||||
|
||||
struct Metrics {
|
||||
const float x;
|
||||
|
@ -32,7 +29,7 @@ struct Metrics {
|
|||
const float r;
|
||||
};
|
||||
|
||||
static Metrics calculate_metrics(const Frame frame) {
|
||||
static Metrics calculate_metrics(const Frame& frame) {
|
||||
/* TODO: Yikes! M0 doesn't have floating point, so this code is
|
||||
* expensive! On the other hand, it seems to be working well (and
|
||||
* fast *enough*?), so maybe leave it alone at least for now.
|
||||
|
@ -69,14 +66,7 @@ static Metrics calculate_metrics(const Frame frame) {
|
|||
};
|
||||
}
|
||||
|
||||
ui::Point Manager::raw_point() const {
|
||||
return {
|
||||
static_cast<ui::Coord>(cmx),
|
||||
static_cast<ui::Coord>(cmy)
|
||||
};
|
||||
}
|
||||
|
||||
void Manager::feed(const Frame frame) {
|
||||
void Manager::feed(const Frame& frame) {
|
||||
// touch_debounce.feed(touch_raw);
|
||||
const auto touch_raw = frame.touch;
|
||||
//const auto touch_stable = touch_debounce.state();
|
||||
|
@ -91,9 +81,6 @@ void Manager::feed(const Frame frame) {
|
|||
// TODO: Add touch pressure hysteresis?
|
||||
touch_pressure = (metrics.r < r_touch_threshold);
|
||||
if( touch_pressure ) {
|
||||
cmx = metrics.x*100;
|
||||
cmy = metrics.y*100;
|
||||
|
||||
const float x = width_pixels * (metrics.x - calib_x_low) / calib_x_range;
|
||||
filter_x.feed(x);
|
||||
const float y = height_pixels * (calib_y_high - metrics.y) / calib_y_range;
|
||||
|
|
|
@ -165,7 +165,7 @@ class Manager {
|
|||
public:
|
||||
std::function<void(ui::TouchEvent)> on_event;
|
||||
|
||||
void feed(const Frame frame);
|
||||
void feed(const Frame& frame);
|
||||
|
||||
private:
|
||||
enum State {
|
||||
|
|
|
@ -23,13 +23,10 @@
|
|||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "crc.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace tpms {
|
||||
|
@ -54,96 +51,20 @@ std::string temperature(Temperature temperature) {
|
|||
|
||||
} /* namespace format */
|
||||
|
||||
Timestamp Packet::received_at() const {
|
||||
return packet_.timestamp();
|
||||
}
|
||||
|
||||
ManchesterFormatted Packet::symbols_formatted() const {
|
||||
return format_manchester(decoder_);
|
||||
}
|
||||
|
||||
Optional<Reading> Packet::reading() const {
|
||||
const auto length = crc_valid_length();
|
||||
|
||||
switch(length) {
|
||||
case 64:
|
||||
return Reading {
|
||||
Reading::Type::FLM_64,
|
||||
reader_.read(0, 32),
|
||||
Pressure { static_cast<int>(reader_.read(32, 8)) * 4 / 3 },
|
||||
Temperature { static_cast<int>(reader_.read(40, 8) & 0x7f) - 50 }
|
||||
};
|
||||
|
||||
case 72:
|
||||
return Reading {
|
||||
Reading::Type::FLM_72,
|
||||
reader_.read(0, 32),
|
||||
Pressure { static_cast<int>(reader_.read(40, 8)) * 4 / 3 },
|
||||
Temperature { static_cast<int>(reader_.read(48, 8)) - 50 }
|
||||
};
|
||||
|
||||
case 80:
|
||||
return Reading {
|
||||
Reading::Type::FLM_80,
|
||||
reader_.read(8, 32),
|
||||
Pressure { static_cast<int>(reader_.read(48, 8)) * 4 / 3 },
|
||||
Temperature { static_cast<int>(reader_.read(56, 8)) - 50 }
|
||||
};
|
||||
|
||||
default:
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
size_t Packet::crc_valid_length() const {
|
||||
constexpr uint32_t checksum_bytes = 0b1111111;
|
||||
constexpr uint32_t crc_72_bytes = 0b111111111;
|
||||
constexpr uint32_t crc_80_bytes = 0b1111111110;
|
||||
|
||||
std::array<uint8_t, 10> bytes;
|
||||
for(size_t i=0; i<bytes.size(); i++) {
|
||||
bytes[i] = reader_.read(i * 8, 8);
|
||||
}
|
||||
|
||||
uint32_t checksum = 0;
|
||||
CRC<uint8_t> crc_72 { 0x01, 0x00 };
|
||||
CRC<uint8_t> crc_80 { 0x01, 0x00 };
|
||||
|
||||
for(size_t i=0; i<bytes.size(); i++) {
|
||||
const uint32_t byte_mask = 1 << i;
|
||||
const auto byte = bytes[i];
|
||||
|
||||
if( checksum_bytes & byte_mask ) {
|
||||
checksum += byte;
|
||||
}
|
||||
if( crc_72_bytes & byte_mask ) {
|
||||
crc_72.process_byte(byte);
|
||||
}
|
||||
if( crc_80_bytes & byte_mask ) {
|
||||
crc_80.process_byte(byte);
|
||||
}
|
||||
}
|
||||
|
||||
if( crc_80.checksum() == 0 ) {
|
||||
return 80;
|
||||
} else if( crc_72.checksum() == 0 ) {
|
||||
return 72;
|
||||
} else if( (checksum & 0xff) == bytes[7] ) {
|
||||
return 64;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace tpms */
|
||||
|
||||
void TPMSLogger::on_packet(const tpms::Packet& packet) {
|
||||
TPMSLogger::TPMSLogger(
|
||||
const std::string& file_path
|
||||
) : log_file { file_path }
|
||||
{
|
||||
}
|
||||
|
||||
void TPMSLogger::on_packet(const tpms::Packet& packet, const uint32_t target_frequency) {
|
||||
const auto hex_formatted = packet.symbols_formatted();
|
||||
|
||||
if( log_file.is_ready() ) {
|
||||
const auto tuning_frequency = receiver_model.tuning_frequency();
|
||||
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
|
||||
const auto tuning_frequency_str = to_string_dec_uint(tuning_frequency, 10);
|
||||
const auto tuning_frequency_str = to_string_dec_uint(target_frequency, 10);
|
||||
|
||||
std::string entry = tuning_frequency_str + " FSK 38.4 19.2 " + hex_formatted.data + "/" + hex_formatted.errors;
|
||||
log_file.write_entry(packet.received_at(), entry);
|
||||
|
@ -235,25 +156,32 @@ TPMSAppView::TPMSAppView(NavigationView&) {
|
|||
[this](Message* const p) {
|
||||
const auto message = static_cast<const TPMSPacketMessage*>(p);
|
||||
const tpms::Packet packet { message->packet };
|
||||
this->on_packet(packet);
|
||||
this->on_packet(message->signal_type, packet);
|
||||
}
|
||||
);
|
||||
|
||||
receiver_model.set_baseband_configuration({
|
||||
radio::enable({
|
||||
tuning_frequency(),
|
||||
sampling_rate,
|
||||
baseband_bandwidth,
|
||||
rf::Direction::Receive,
|
||||
false, 32, 32,
|
||||
1,
|
||||
});
|
||||
|
||||
baseband::start({
|
||||
.mode = 5,
|
||||
.sampling_rate = 2457600,
|
||||
.sampling_rate = sampling_rate,
|
||||
.decimation_factor = 1,
|
||||
});
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
receiver_model.set_rf_amp(false);
|
||||
receiver_model.set_lna(32);
|
||||
receiver_model.set_vga(32);
|
||||
receiver_model.set_tuning_frequency(315000000);
|
||||
receiver_model.enable();
|
||||
|
||||
logger = std::make_unique<TPMSLogger>("tpms.txt");
|
||||
}
|
||||
|
||||
TPMSAppView::~TPMSAppView() {
|
||||
receiver_model.disable();
|
||||
baseband::stop();
|
||||
radio::disable();
|
||||
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::TPMSPacket);
|
||||
}
|
||||
|
||||
|
@ -266,10 +194,12 @@ void TPMSAppView::set_parent_rect(const Rect new_parent_rect) {
|
|||
recent_entries_view.set_parent_rect({ 0, 0, new_parent_rect.width(), new_parent_rect.height() });
|
||||
}
|
||||
|
||||
void TPMSAppView::on_packet(const tpms::Packet& packet) {
|
||||
logger.on_packet(packet);
|
||||
void TPMSAppView::on_packet(const tpms::SignalType signal_type, const tpms::Packet& packet) {
|
||||
if( logger ) {
|
||||
logger->on_packet(packet, target_frequency());
|
||||
}
|
||||
|
||||
const auto reading_opt = packet.reading();
|
||||
const auto reading_opt = packet.reading(signal_type);
|
||||
if( reading_opt.is_valid() ) {
|
||||
const auto reading = reading_opt.value();
|
||||
recent.on_packet({ reading.type(), reading.id() }, reading);
|
||||
|
@ -282,4 +212,12 @@ void TPMSAppView::on_show_list() {
|
|||
recent_entries_view.focus();
|
||||
}
|
||||
|
||||
uint32_t TPMSAppView::target_frequency() const {
|
||||
return initial_target_frequency;
|
||||
}
|
||||
|
||||
uint32_t TPMSAppView::tuning_frequency() const {
|
||||
return target_frequency() - (sampling_rate / 4);
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
|
|
@ -25,127 +25,11 @@
|
|||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
|
||||
#include "field_reader.hpp"
|
||||
#include "baseband_packet.hpp"
|
||||
#include "manchester.hpp"
|
||||
#include "log_file.hpp"
|
||||
|
||||
#include "recent_entries.hpp"
|
||||
|
||||
#include "optional.hpp"
|
||||
|
||||
#include "units.hpp"
|
||||
using units::Temperature;
|
||||
using units::Pressure;
|
||||
|
||||
namespace tpms {
|
||||
|
||||
class TransponderID {
|
||||
public:
|
||||
constexpr TransponderID(
|
||||
) : id_ { 0 }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr TransponderID(
|
||||
const uint32_t id
|
||||
) : id_ { id }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr uint32_t value() const {
|
||||
return id_;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t id_;
|
||||
};
|
||||
|
||||
class Reading {
|
||||
public:
|
||||
enum Type {
|
||||
None = 0,
|
||||
FLM_64 = 1,
|
||||
FLM_72 = 2,
|
||||
FLM_80 = 3,
|
||||
};
|
||||
|
||||
constexpr Reading(
|
||||
) : type_ { Type::None }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Reading(
|
||||
Type type,
|
||||
TransponderID id
|
||||
) : type_ { type },
|
||||
id_ { id }
|
||||
{
|
||||
}
|
||||
|
||||
constexpr Reading(
|
||||
Type type,
|
||||
TransponderID id,
|
||||
Optional<Pressure> pressure = { },
|
||||
Optional<Temperature> temperature = { }
|
||||
) : type_ { type },
|
||||
id_ { id },
|
||||
pressure_ { pressure },
|
||||
temperature_ { temperature }
|
||||
{
|
||||
}
|
||||
|
||||
Type type() const {
|
||||
return type_;
|
||||
}
|
||||
|
||||
TransponderID id() const {
|
||||
return id_;
|
||||
}
|
||||
|
||||
Optional<Pressure> pressure() const {
|
||||
return pressure_;
|
||||
}
|
||||
|
||||
Optional<Temperature> temperature() const {
|
||||
return temperature_;
|
||||
}
|
||||
|
||||
private:
|
||||
Type type_ { Type::None };
|
||||
TransponderID id_ { 0 };
|
||||
Optional<Pressure> pressure_ { };
|
||||
Optional<Temperature> temperature_ { };
|
||||
};
|
||||
|
||||
class Packet {
|
||||
public:
|
||||
constexpr Packet(
|
||||
const baseband::Packet& packet
|
||||
) : packet_ { packet },
|
||||
decoder_ { packet_, 0 },
|
||||
reader_ { decoder_ }
|
||||
{
|
||||
}
|
||||
|
||||
Timestamp received_at() const;
|
||||
|
||||
ManchesterFormatted symbols_formatted() const;
|
||||
|
||||
Optional<Reading> reading() const;
|
||||
|
||||
private:
|
||||
using Reader = FieldReader<ManchesterDecoder, BitRemapNone>;
|
||||
|
||||
const baseband::Packet packet_;
|
||||
const ManchesterDecoder decoder_;
|
||||
|
||||
const Reader reader_;
|
||||
|
||||
size_t crc_valid_length() const;
|
||||
};
|
||||
|
||||
} /* namespace tpms */
|
||||
#include "tpms_packet.hpp"
|
||||
|
||||
namespace std {
|
||||
|
||||
|
@ -186,10 +70,12 @@ using TPMSRecentEntries = RecentEntries<tpms::Reading, TPMSRecentEntry>;
|
|||
|
||||
class TPMSLogger {
|
||||
public:
|
||||
void on_packet(const tpms::Packet& packet);
|
||||
TPMSLogger(const std::string& file_path);
|
||||
|
||||
void on_packet(const tpms::Packet& packet, const uint32_t target_frequency);
|
||||
|
||||
private:
|
||||
LogFile log_file { "tpms.txt" };
|
||||
LogFile log_file;
|
||||
};
|
||||
|
||||
namespace ui {
|
||||
|
@ -212,13 +98,20 @@ public:
|
|||
std::string title() const override { return "TPMS"; };
|
||||
|
||||
private:
|
||||
static constexpr uint32_t initial_target_frequency = 315000000;
|
||||
static constexpr uint32_t sampling_rate = 2457600;
|
||||
static constexpr uint32_t baseband_bandwidth = 1750000;
|
||||
|
||||
TPMSRecentEntries recent;
|
||||
TPMSLogger logger;
|
||||
std::unique_ptr<TPMSLogger> logger;
|
||||
|
||||
TPMSRecentEntriesView recent_entries_view { recent };
|
||||
|
||||
void on_packet(const tpms::Packet& packet);
|
||||
void on_packet(const tpms::SignalType signal_type, const tpms::Packet& packet);
|
||||
void on_show_list();
|
||||
|
||||
uint32_t target_frequency() const;
|
||||
uint32_t tuning_frequency() const;
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
|
|
@ -35,7 +35,7 @@ void Console::clear() {
|
|||
display.scroll_set_position(0);
|
||||
}
|
||||
|
||||
void Console::write(const std::string message) {
|
||||
void Console::write(const std::string& message) {
|
||||
const Style& s = style();
|
||||
const Font& font = s.font;
|
||||
const auto rect = screen_rect();
|
||||
|
@ -58,7 +58,7 @@ void Console::write(const std::string message) {
|
|||
}
|
||||
}
|
||||
|
||||
void Console::writeln(const std::string message) {
|
||||
void Console::writeln(const std::string& message) {
|
||||
write(message);
|
||||
crlf();
|
||||
}
|
||||
|
|
|
@ -33,8 +33,8 @@ namespace ui {
|
|||
class Console : public Widget {
|
||||
public:
|
||||
void clear();
|
||||
void write(const std::string message);
|
||||
void writeln(const std::string message);
|
||||
void write(const std::string& message);
|
||||
void writeln(const std::string& message);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
|
|
|
@ -26,6 +26,10 @@
|
|||
#include "radio.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "audio.hpp"
|
||||
|
||||
#include "ui_sd_card_debug.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
/* DebugMemoryView *******************************************************/
|
||||
|
@ -33,8 +37,8 @@ namespace ui {
|
|||
DebugMemoryView::DebugMemoryView(NavigationView& nav) {
|
||||
add_children({ {
|
||||
&text_title,
|
||||
&text_label_m0_free,
|
||||
&text_label_m0_free_value,
|
||||
&text_label_m0_core_free,
|
||||
&text_label_m0_core_free_value,
|
||||
&text_label_m0_heap_fragmented_free,
|
||||
&text_label_m0_heap_fragmented_free_value,
|
||||
&text_label_m0_heap_fragments,
|
||||
|
@ -42,8 +46,8 @@ DebugMemoryView::DebugMemoryView(NavigationView& nav) {
|
|||
&button_done
|
||||
} });
|
||||
|
||||
const auto m0_free = chCoreStatus();
|
||||
text_label_m0_free_value.set(to_string_dec_uint(m0_free, 5));
|
||||
const auto m0_core_free = chCoreStatus();
|
||||
text_label_m0_core_free_value.set(to_string_dec_uint(m0_core_free, 5));
|
||||
|
||||
size_t m0_fragmented_free_space = 0;
|
||||
const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space);
|
||||
|
@ -240,68 +244,39 @@ void RegistersView::focus() {
|
|||
button_done.focus();
|
||||
}
|
||||
|
||||
char hexify(char in) {
|
||||
if (in > 9) in += 7;
|
||||
return in + 0x30;
|
||||
}
|
||||
/* DebugPeripheralsMenuView **********************************************/
|
||||
|
||||
DebugLCRView::DebugLCRView(NavigationView& nav, char * lcrstring, uint8_t checksum) {
|
||||
char cstr[15] = "Checksum: 0x ";
|
||||
|
||||
add_children({ {
|
||||
&text_lcr1,
|
||||
&text_lcr2,
|
||||
&text_lcr3,
|
||||
&text_lcr4,
|
||||
&text_lcr5,
|
||||
&text_checksum,
|
||||
&button_done
|
||||
DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) {
|
||||
add_items<4>({ {
|
||||
{ "RFFC5072", [&nav](){ nav.push<RegistersView>(
|
||||
"RFFC5072", RegistersWidgetConfig { 31, 2, 4, 4 },
|
||||
[](const size_t register_number) { return radio::debug::first_if::register_read(register_number); }
|
||||
); } },
|
||||
{ "MAX2837", [&nav](){ nav.push<RegistersView>(
|
||||
"MAX2837", RegistersWidgetConfig { 32, 2, 3, 4 },
|
||||
[](const size_t register_number) { return radio::debug::second_if::register_read(register_number); }
|
||||
); } },
|
||||
{ "Si5351C", [&nav](){ nav.push<RegistersView>(
|
||||
"Si5351C", RegistersWidgetConfig { 96, 2, 2, 8 },
|
||||
[](const size_t register_number) { return portapack::clock_generator.read_register(register_number); }
|
||||
); } },
|
||||
{ "WM8731", [&nav](){ nav.push<RegistersView>(
|
||||
"WM8731", RegistersWidgetConfig { audio::debug::reg_count(), 1, 3, 4 },
|
||||
[](const size_t register_number) { return audio::debug::reg_read(register_number); }
|
||||
); } },
|
||||
} });
|
||||
|
||||
std::string b = std::string(lcrstring);
|
||||
|
||||
text_lcr1.set(b.substr(8+(0*26),26));
|
||||
if (strlen(lcrstring) > 34) text_lcr2.set(b.substr(8+(1*26),26));
|
||||
if (strlen(lcrstring) > 34+26) text_lcr3.set(b.substr(8+(2*26),26));
|
||||
if (strlen(lcrstring) > 34+26+26) text_lcr4.set(b.substr(8+(3*26),26));
|
||||
if (strlen(lcrstring) > 34+26+26+26) text_lcr5.set(b.substr(8+(4*26),26));
|
||||
|
||||
cstr[12] = hexify(checksum >> 4);
|
||||
cstr[13] = hexify(checksum & 15);
|
||||
|
||||
text_checksum.set(cstr);
|
||||
|
||||
button_done.on_select = [&nav](Button&){ nav.pop(); };
|
||||
}
|
||||
|
||||
void DebugLCRView::focus() {
|
||||
button_done.focus();
|
||||
on_left = [&nav](){ nav.pop(); };
|
||||
}
|
||||
|
||||
/* DebugMenuView *********************************************************/
|
||||
|
||||
DebugMenuView::DebugMenuView(NavigationView& nav) {
|
||||
add_items<8>({ {
|
||||
{ "Memory", ui::Color::white(), [&nav](){ nav.push<DebugMemoryView>(); } },
|
||||
{ "Radio State", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "SD Card", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "RFFC5072", ui::Color::white(), [&nav](){ nav.push<RegistersView>(
|
||||
"RFFC5072", RegistersWidgetConfig { 31, 2, 4, 4 },
|
||||
[](const size_t register_number) { return radio::first_if.read(register_number); }
|
||||
); } },
|
||||
{ "MAX2837", ui::Color::white(), [&nav](){ nav.push<RegistersView>(
|
||||
"MAX2837", RegistersWidgetConfig { 32, 2, 3, 4 },
|
||||
[](const size_t register_number) { return radio::second_if.read(register_number); }
|
||||
); } },
|
||||
{ "Si5351C", ui::Color::white(), [&nav](){ nav.push<RegistersView>(
|
||||
"Si5351C", RegistersWidgetConfig { 96, 2, 2, 8 },
|
||||
[](const size_t register_number) { return portapack::clock_generator.read_register(register_number); }
|
||||
); } },
|
||||
{ "WM8731", ui::Color::white(), [&nav](){ nav.push<RegistersView>(
|
||||
"WM8731", RegistersWidgetConfig { wolfson::wm8731::reg_count, 1, 3, 4 },
|
||||
[](const size_t register_number) { return portapack::audio_codec.read(register_number); }
|
||||
); } },
|
||||
{ "Temperature", ui::Color::white(), [&nav](){ nav.push<TemperatureView>(); } },
|
||||
add_items<5>({ {
|
||||
{ "Memory", [&nav](){ nav.push<DebugMemoryView>(); } },
|
||||
{ "Radio State", [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "SD Card", [&nav](){ nav.push<SDCardDebugView>(); } },
|
||||
{ "Peripherals", [&nav](){ nav.push<DebugPeripheralsMenuView>(); } },
|
||||
{ "Temperature", [&nav](){ nav.push<TemperatureView>(); } },
|
||||
} });
|
||||
on_left = [&nav](){ nav.pop(); };
|
||||
}
|
||||
|
|
|
@ -36,8 +36,6 @@
|
|||
#include <utility>
|
||||
|
||||
namespace ui {
|
||||
|
||||
char hexify(char in);
|
||||
|
||||
class DebugMemoryView : public View {
|
||||
public:
|
||||
|
@ -51,12 +49,12 @@ private:
|
|||
"Memory",
|
||||
};
|
||||
|
||||
Text text_label_m0_free {
|
||||
{ 0, 128, 104, 16 },
|
||||
"M0 Free Bytes",
|
||||
Text text_label_m0_core_free {
|
||||
{ 0, 128, 144, 16 },
|
||||
"M0 Core Free Bytes",
|
||||
};
|
||||
|
||||
Text text_label_m0_free_value {
|
||||
Text text_label_m0_core_free_value {
|
||||
{ 200, 128, 40, 16 },
|
||||
};
|
||||
|
||||
|
@ -279,6 +277,11 @@ private:
|
|||
};
|
||||
};
|
||||
|
||||
class DebugPeripheralsMenuView : public MenuView {
|
||||
public:
|
||||
DebugPeripheralsMenuView(NavigationView& nav);
|
||||
};
|
||||
|
||||
class DebugMenuView : public MenuView {
|
||||
public:
|
||||
DebugMenuView(NavigationView& nav);
|
||||
|
|
|
@ -32,17 +32,19 @@ void MenuItemView::select() {
|
|||
}
|
||||
|
||||
void MenuItemView::highlight() {
|
||||
set_highlight(true);
|
||||
set_highlighted(true);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void MenuItemView::unhighlight() {
|
||||
set_highlight(false);
|
||||
set_highlighted(false);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
void MenuItemView::paint(Painter& painter) {
|
||||
const auto r = screen_rect();
|
||||
|
||||
const auto paint_style = (flags.highlighted && parent()->has_focus()) ? style().invert() : style();
|
||||
const auto paint_style = (highlighted() && parent()->has_focus()) ? style().invert() : style();
|
||||
|
||||
const auto font_height = paint_style.font.line_height();
|
||||
|
||||
|
@ -68,11 +70,6 @@ void MenuItemView::paint(Painter& painter) {
|
|||
);
|
||||
}
|
||||
|
||||
void MenuItemView::set_highlight(const bool value) {
|
||||
flags.highlighted = value;
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
/* MenuView **************************************************************/
|
||||
|
||||
MenuView::~MenuView() {
|
||||
|
|
|
@ -36,6 +36,10 @@ struct MenuItem {
|
|||
std::string text;
|
||||
ui::Color color;
|
||||
std::function<void(void)> on_select;
|
||||
|
||||
// TODO: Prevent default-constructed MenuItems.
|
||||
// I managed to construct a menu with three extra, unspecified menu items
|
||||
// in the array that were default constructed...
|
||||
};
|
||||
|
||||
class MenuItemView : public Widget {
|
||||
|
@ -54,8 +58,6 @@ public:
|
|||
|
||||
private:
|
||||
const MenuItem item;
|
||||
|
||||
void set_highlight(const bool value);
|
||||
};
|
||||
|
||||
class MenuView : public View {
|
||||
|
@ -63,7 +65,7 @@ public:
|
|||
std::function<void(void)> on_left;
|
||||
|
||||
MenuView() {
|
||||
flags.focusable = true;
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
~MenuView();
|
||||
|
@ -71,7 +73,7 @@ public:
|
|||
void add_item(const MenuItem item);
|
||||
|
||||
template<size_t N>
|
||||
void add_items(const std::array<MenuItem, N> items) {
|
||||
void add_items(const std::array<MenuItem, N>& items) {
|
||||
for(const auto& item : items) {
|
||||
add_item(item);
|
||||
}
|
||||
|
|
|
@ -23,37 +23,23 @@
|
|||
|
||||
#include "portapack.hpp"
|
||||
#include "event_m0.hpp"
|
||||
#include "receiver_model.hpp"
|
||||
#include "transmitter_model.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
#include "splash.hpp"
|
||||
|
||||
#include "ui_about.hpp"
|
||||
#include "ui_setup.hpp"
|
||||
#include "ui_debug.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_rds.hpp"
|
||||
#include "ui_lcr.hpp"
|
||||
#include "ui_whistle.hpp"
|
||||
#include "ui_jammer.hpp"
|
||||
#include "ui_loadmodule.hpp"
|
||||
#include "ui_afskrx.hpp"
|
||||
#include "ui_xylos.hpp"
|
||||
#include "ui_sigfrx.hpp"
|
||||
#include "ui_numbers.hpp"
|
||||
|
||||
#include "analog_audio_app.hpp"
|
||||
#include "ais_app.hpp"
|
||||
#include "ert_app.hpp"
|
||||
#include "tpms_app.hpp"
|
||||
#include "capture_app.hpp"
|
||||
|
||||
#include "m4_startup.hpp"
|
||||
#include "spi_image.hpp"
|
||||
#include "core_control.hpp"
|
||||
|
||||
#include "modules.h"
|
||||
|
||||
using namespace portapack;
|
||||
#include "file.hpp"
|
||||
#include "png_writer.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
|
@ -63,6 +49,7 @@ SystemStatusView::SystemStatusView() {
|
|||
add_children({ {
|
||||
&button_back,
|
||||
&title,
|
||||
&button_camera,
|
||||
&button_sleep,
|
||||
&sd_card_status_view,
|
||||
} });
|
||||
|
@ -73,14 +60,19 @@ SystemStatusView::SystemStatusView() {
|
|||
}
|
||||
};
|
||||
|
||||
button_camera.on_select = [this](ImageButton&) {
|
||||
this->on_camera();
|
||||
};
|
||||
|
||||
button_sleep.on_select = [this](ImageButton&) {
|
||||
DisplaySleepMessage message;
|
||||
EventDispatcher::message_map().send(&message);
|
||||
};
|
||||
}
|
||||
|
||||
void SystemStatusView::set_back_visible(bool new_value) {
|
||||
button_back.hidden(!new_value);
|
||||
void SystemStatusView::set_back_enabled(bool new_value) {
|
||||
button_back.set_text(new_value ? back_text_enabled : back_text_disabled);
|
||||
button_back.set_focusable(new_value);
|
||||
}
|
||||
|
||||
void SystemStatusView::set_title(const std::string new_value) {
|
||||
|
@ -91,6 +83,21 @@ void SystemStatusView::set_title(const std::string new_value) {
|
|||
}
|
||||
}
|
||||
|
||||
void SystemStatusView::on_camera() {
|
||||
const auto filename = next_filename_matching_pattern("SCR_????.PNG");
|
||||
if( filename.empty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
PNGWriter png { filename };
|
||||
|
||||
for(int i=0; i<320; i++) {
|
||||
std::array<ColorRGB888, 240> row;
|
||||
portapack::display.read_pixels({ 0, i, 240, 1 }, row);
|
||||
png.write_scanline(row);
|
||||
}
|
||||
}
|
||||
|
||||
/* Navigation ************************************************************/
|
||||
|
||||
bool NavigationView::is_top() const {
|
||||
|
@ -108,10 +115,6 @@ View* NavigationView::push_view(std::unique_ptr<View> new_view) {
|
|||
return p;
|
||||
}
|
||||
|
||||
void NavigationView::push(View* v) {
|
||||
push_view(std::unique_ptr<View>(v));
|
||||
}
|
||||
|
||||
void NavigationView::pop() {
|
||||
// Can't pop last item from stack.
|
||||
if( view_stack.size() > 1 ) {
|
||||
|
@ -153,25 +156,37 @@ void NavigationView::focus() {
|
|||
|
||||
TranspondersMenuView::TranspondersMenuView(NavigationView& nav) {
|
||||
add_items<3>({ {
|
||||
{ "AIS: Boats", ui::Color::white(), [&nav](){ nav.push<AISAppView>(); } },
|
||||
{ "ERT: Utility Meters", ui::Color::white(), [&nav](){ nav.push<ERTAppView>(); } },
|
||||
{ "TPMS: Cars", ui::Color::white(), [&nav](){ nav.push<TPMSAppView>(); } },
|
||||
{ "AIS: Boats", [&nav](){ nav.push<AISAppView>(); } },
|
||||
{ "ERT: Utility Meters", [&nav](){ nav.push<ERTAppView>(); } },
|
||||
{ "TPMS: Cars", [&nav](){ nav.push<TPMSAppView>(); } },
|
||||
} });
|
||||
on_left = [&nav](){ nav.pop(); };
|
||||
}
|
||||
|
||||
/* ReceiverMenuView ******************************************************/
|
||||
|
||||
ReceiverMenuView::ReceiverMenuView(NavigationView& nav) {
|
||||
add_items<2>({ {
|
||||
{ "Audio", ui::Color::white(), [&nav](){ nav.push<AnalogAudioView>(); } },
|
||||
{ "Transponders", ui::Color::white(), [&nav](){ nav.push<TranspondersMenuView>(); } },
|
||||
{ "Audio", [&nav](){ nav.push<AnalogAudioView>(); } },
|
||||
{ "Transponders", [&nav](){ nav.push<TranspondersMenuView>(); } },
|
||||
} });
|
||||
on_left = [&nav](){ nav.pop(); };
|
||||
}
|
||||
|
||||
/* SystemMenuView ********************************************************/
|
||||
|
||||
SystemMenuView::SystemMenuView(NavigationView& nav) {
|
||||
add_items<10>({ {
|
||||
add_items<7>({ {
|
||||
{ "Receiver", [&nav](){ nav.push<ReceiverMenuView>(); } },
|
||||
{ "Capture", [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "Analyze", [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "Setup", [&nav](){ nav.push<SetupMenuView>(); } },
|
||||
{ "About", [&nav](){ nav.push<AboutView>(); } },
|
||||
{ "Debug", [&nav](){ nav.push<DebugMenuView>(); } },
|
||||
{ "HackRF", [&nav](){ nav.push<HackRFFirmwareView>(); } },
|
||||
} });
|
||||
|
||||
/* add_items<10>({ {
|
||||
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(false); } },
|
||||
{ "Receiver", ui::Color::cyan(), [&nav](){ nav.push<LoadModuleView>(md5_baseband, new ReceiverMenuView(nav)); } },
|
||||
//{ "Nordic/BTLE RX", ui::Color::cyan(), [&nav](){ nav.push(new NotImplementedView { nav }); } },
|
||||
|
@ -190,7 +205,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
|
|||
{ "About", ui::Color::white(), [&nav](){ nav.push<AboutView>(); } },
|
||||
{ "Debug", ui::Color::white(), [&nav](){ nav.push<DebugMenuView>(); } },
|
||||
{ "HackRF", ui::Color::white(), [&nav](){ nav.push<HackRFFirmwareView>(); } },
|
||||
} });
|
||||
} });*/
|
||||
}
|
||||
|
||||
/* SystemView ************************************************************/
|
||||
|
@ -207,7 +222,7 @@ SystemView::SystemView(
|
|||
) : View { parent_rect },
|
||||
context_(context)
|
||||
{
|
||||
style_ = &style_default;
|
||||
set_style(&style_default);
|
||||
|
||||
constexpr ui::Dim status_view_height = 16;
|
||||
|
||||
|
@ -226,7 +241,7 @@ SystemView::SystemView(
|
|||
{ parent_rect.width(), static_cast<ui::Dim>(parent_rect.height() - status_view_height) }
|
||||
});
|
||||
navigation_view.on_view_changed = [this](const View& new_view) {
|
||||
this->status_view.set_back_visible(!this->navigation_view.is_top());
|
||||
this->status_view.set_back_enabled(!this->navigation_view.is_top());
|
||||
this->status_view.set_title(new_view.title());
|
||||
};
|
||||
|
||||
|
|
|
@ -60,21 +60,46 @@ static constexpr Bitmap bitmap_sleep {
|
|||
{ 16, 16 }, bitmap_sleep_data
|
||||
};
|
||||
|
||||
static constexpr uint8_t bitmap_camera_data[] = {
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0xcc, 0x03,
|
||||
0xe8, 0x07,
|
||||
0xfc, 0x3f,
|
||||
0x3c, 0x3c,
|
||||
0x9c, 0x39,
|
||||
0xdc, 0x3b,
|
||||
0xdc, 0x3b,
|
||||
0x9c, 0x39,
|
||||
0x3c, 0x3c,
|
||||
0xfc, 0x3f,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
0x00, 0x00,
|
||||
};
|
||||
|
||||
static constexpr Bitmap bitmap_camera {
|
||||
{ 16, 16 }, bitmap_camera_data
|
||||
};
|
||||
|
||||
class SystemStatusView : public View {
|
||||
public:
|
||||
std::function<void(void)> on_back;
|
||||
|
||||
SystemStatusView();
|
||||
|
||||
void set_back_visible(bool new_value);
|
||||
void set_back_enabled(bool new_value);
|
||||
void set_title(const std::string new_value);
|
||||
|
||||
private:
|
||||
static constexpr auto default_title = "PortaPack";
|
||||
static constexpr auto back_text_enabled = " < ";
|
||||
static constexpr auto back_text_disabled = " * ";
|
||||
|
||||
Button button_back {
|
||||
{ 0 * 8, 0 * 16, 3 * 8, 16 },
|
||||
" < ",
|
||||
back_text_disabled,
|
||||
};
|
||||
|
||||
Text title {
|
||||
|
@ -82,6 +107,13 @@ private:
|
|||
default_title,
|
||||
};
|
||||
|
||||
ImageButton button_camera {
|
||||
{ 22 * 8, 0, 2 * 8, 1 * 16 },
|
||||
&bitmap_camera,
|
||||
Color::white(),
|
||||
Color::black()
|
||||
};
|
||||
|
||||
ImageButton button_sleep {
|
||||
{ 25 * 8, 0, 2 * 8, 1 * 16 },
|
||||
&bitmap_sleep,
|
||||
|
@ -92,6 +124,8 @@ private:
|
|||
SDCardStatusView sd_card_status_view {
|
||||
{ 28 * 8, 0 * 16, 2 * 8, 1 * 16 }
|
||||
};
|
||||
|
||||
void on_camera();
|
||||
};
|
||||
|
||||
class NavigationView : public View {
|
||||
|
|
|
@ -38,7 +38,7 @@ FrequencyField::FrequencyField(
|
|||
length_ { 11 },
|
||||
range(rf::tuning_range)
|
||||
{
|
||||
flags.focusable = true;
|
||||
set_focusable(true);
|
||||
}
|
||||
|
||||
rf::Frequency FrequencyField::value() const {
|
||||
|
@ -318,4 +318,24 @@ void LNAGainField::on_focus() {
|
|||
}
|
||||
}
|
||||
|
||||
/* VGAGainField **********************************************************/
|
||||
|
||||
VGAGainField::VGAGainField(
|
||||
Point parent_pos
|
||||
) : NumberField {
|
||||
parent_pos, 2,
|
||||
{ max2837::vga::gain_db_range.minimum, max2837::vga::gain_db_range.maximum },
|
||||
max2837::vga::gain_db_step,
|
||||
' ',
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
void VGAGainField::on_focus() {
|
||||
//Widget::on_focus();
|
||||
if( on_show_options ) {
|
||||
on_show_options();
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
|
|
@ -315,6 +315,15 @@ public:
|
|||
void on_focus() override;
|
||||
};
|
||||
|
||||
class VGAGainField : public NumberField {
|
||||
public:
|
||||
std::function<void(void)> on_show_options;
|
||||
|
||||
VGAGainField(Point parent_pos);
|
||||
|
||||
void on_focus() override;
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_RECEIVER_H__*/
|
||||
|
|
409
firmware/application/ui_sd_card_debug.cpp
Normal file
409
firmware/application/ui_sd_card_debug.cpp
Normal file
|
@ -0,0 +1,409 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "ui_sd_card_debug.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "file.hpp"
|
||||
#include "lfsr_random.hpp"
|
||||
|
||||
#include "ff.h"
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
|
||||
class SDCardTestThread {
|
||||
public:
|
||||
enum Result {
|
||||
FailCompare = -8,
|
||||
FailReadIncomplete = -7,
|
||||
FailWriteIncomplete = -6,
|
||||
FailAbort = -5,
|
||||
FailFileOpenRead = -4,
|
||||
FailFileOpenWrite = -3,
|
||||
FailHeap = -2,
|
||||
FailThread = -1,
|
||||
Incomplete = 0,
|
||||
OK = 1,
|
||||
};
|
||||
|
||||
struct Stats {
|
||||
halrtcnt_t write_duration_min { 0 };
|
||||
halrtcnt_t write_duration_max { 0 };
|
||||
halrtcnt_t write_test_duration { 0 };
|
||||
size_t write_bytes { 0 };
|
||||
size_t write_count { 0 };
|
||||
|
||||
halrtcnt_t read_duration_min { 0 };
|
||||
halrtcnt_t read_duration_max { 0 };
|
||||
halrtcnt_t read_test_duration { 0 };
|
||||
size_t read_bytes { 0 };
|
||||
size_t read_count { 0 };
|
||||
};
|
||||
|
||||
SDCardTestThread(
|
||||
) {
|
||||
thread = chThdCreateFromHeap(NULL, 2048, NORMALPRIO + 10, SDCardTestThread::static_fn, this);
|
||||
}
|
||||
|
||||
Result result() const {
|
||||
return _result;
|
||||
}
|
||||
|
||||
const Stats& stats() const {
|
||||
return _stats;
|
||||
}
|
||||
|
||||
~SDCardTestThread() {
|
||||
chThdTerminate(thread);
|
||||
chThdWait(thread);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t write_size = 16384;
|
||||
static constexpr size_t bytes_to_write = 16 * 1024 * 1024;
|
||||
static constexpr size_t bytes_to_read = bytes_to_write;
|
||||
|
||||
static Thread* thread;
|
||||
volatile Result _result { Result::Incomplete };
|
||||
Stats _stats;
|
||||
|
||||
static msg_t static_fn(void* arg) {
|
||||
auto obj = static_cast<SDCardTestThread*>(arg);
|
||||
obj->_result = obj->run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Result run() {
|
||||
const std::string filename { "_PPTEST_.DAT" };
|
||||
|
||||
const auto write_result = write(filename);
|
||||
if( write_result != Result::OK ) {
|
||||
return write_result;
|
||||
}
|
||||
|
||||
if( _stats.write_bytes < bytes_to_write ) {
|
||||
return Result::FailWriteIncomplete;
|
||||
}
|
||||
|
||||
if( chThdShouldTerminate() ) {
|
||||
return Result::FailAbort;
|
||||
}
|
||||
|
||||
const auto read_result = read(filename);
|
||||
if( read_result != Result::OK ) {
|
||||
return read_result;
|
||||
}
|
||||
|
||||
f_unlink(filename.c_str());
|
||||
|
||||
if( _stats.read_bytes < bytes_to_read ) {
|
||||
return Result::FailReadIncomplete;
|
||||
}
|
||||
|
||||
if( chThdShouldTerminate() ) {
|
||||
return Result::FailAbort;
|
||||
}
|
||||
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
Result write(const std::string& filename) {
|
||||
const auto buffer = std::make_unique<std::array<uint8_t, write_size>>();
|
||||
if( !buffer ) {
|
||||
return Result::FailHeap;
|
||||
}
|
||||
|
||||
File file;
|
||||
if( !file.open_for_writing(filename) ) {
|
||||
return Result::FailFileOpenWrite;
|
||||
}
|
||||
|
||||
lfsr_word_t v = 1;
|
||||
|
||||
const halrtcnt_t test_start = halGetCounterValue();
|
||||
while( !chThdShouldTerminate() && file.is_ready() && (_stats.write_bytes < bytes_to_write) ) {
|
||||
lfsr_fill(v,
|
||||
reinterpret_cast<lfsr_word_t*>(buffer->data()),
|
||||
sizeof(*buffer.get()) / sizeof(lfsr_word_t)
|
||||
);
|
||||
|
||||
const halrtcnt_t write_start = halGetCounterValue();
|
||||
if( !file.write(buffer->data(), buffer->size()) ) {
|
||||
break;
|
||||
}
|
||||
const halrtcnt_t write_end = halGetCounterValue();
|
||||
_stats.write_bytes += buffer->size();
|
||||
_stats.write_count++;
|
||||
|
||||
const halrtcnt_t write_duration = write_end - write_start;
|
||||
if( (_stats.write_duration_min == 0) || (write_duration < _stats.write_duration_min) ) {
|
||||
_stats.write_duration_min = write_duration;
|
||||
}
|
||||
if( write_duration > _stats.write_duration_max ) {
|
||||
_stats.write_duration_max = write_duration;
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
const halrtcnt_t test_end = halGetCounterValue();
|
||||
_stats.write_test_duration = test_end - test_start;
|
||||
|
||||
return Result::OK;
|
||||
}
|
||||
|
||||
Result read(const std::string& filename) {
|
||||
const auto buffer = std::make_unique<std::array<uint8_t, write_size>>();
|
||||
if( !buffer ) {
|
||||
return Result::FailHeap;
|
||||
}
|
||||
|
||||
File file;
|
||||
if( !file.open_for_reading(filename) ) {
|
||||
return Result::FailFileOpenRead;
|
||||
}
|
||||
|
||||
lfsr_word_t v = 1;
|
||||
|
||||
const halrtcnt_t test_start = halGetCounterValue();
|
||||
while( !chThdShouldTerminate() && file.is_ready() && (_stats.read_bytes < bytes_to_read) ) {
|
||||
const halrtcnt_t read_start = halGetCounterValue();
|
||||
if( !file.read(buffer->data(), buffer->size()) ) {
|
||||
break;
|
||||
}
|
||||
const halrtcnt_t read_end = halGetCounterValue();
|
||||
_stats.read_bytes += buffer->size();
|
||||
_stats.read_count++;
|
||||
|
||||
const halrtcnt_t read_duration = read_end - read_start;
|
||||
if( (_stats.read_duration_min == 0) || (read_duration < _stats.read_duration_min) ) {
|
||||
_stats.read_duration_min = read_duration;
|
||||
}
|
||||
if( read_duration > _stats.read_duration_max ) {
|
||||
_stats.read_duration_max = read_duration;
|
||||
}
|
||||
|
||||
if( !lfsr_compare(v,
|
||||
reinterpret_cast<lfsr_word_t*>(buffer->data()),
|
||||
sizeof(*buffer.get()) / sizeof(lfsr_word_t))
|
||||
) {
|
||||
return Result::FailCompare;
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
const halrtcnt_t test_end = halGetCounterValue();
|
||||
_stats.read_test_duration = test_end - test_start;
|
||||
|
||||
return Result::OK;
|
||||
}
|
||||
};
|
||||
|
||||
Thread* SDCardTestThread::thread { nullptr };
|
||||
|
||||
namespace ui {
|
||||
|
||||
SDCardDebugView::SDCardDebugView(NavigationView& nav) {
|
||||
add_children({ {
|
||||
&text_title,
|
||||
&text_detected_title,
|
||||
&text_detected_value,
|
||||
&text_bus_width_title,
|
||||
&text_bus_width_value,
|
||||
&text_card_mode_title,
|
||||
&text_card_mode_value,
|
||||
// &text_csd_title,
|
||||
// &text_csd_value,
|
||||
&text_block_size_title,
|
||||
&text_block_size_value,
|
||||
&text_block_count_title,
|
||||
&text_block_count_value,
|
||||
&text_capacity_title,
|
||||
&text_capacity_value,
|
||||
&text_test_write_time_title,
|
||||
&text_test_write_time_value,
|
||||
&text_test_write_rate_title,
|
||||
&text_test_write_rate_value,
|
||||
&text_test_read_time_title,
|
||||
&text_test_read_time_value,
|
||||
&text_test_read_rate_title,
|
||||
&text_test_read_rate_value,
|
||||
&button_test,
|
||||
&button_ok,
|
||||
} });
|
||||
|
||||
button_test.on_select = [this](Button&){ this->on_test(); };
|
||||
button_ok.on_select = [&nav](Button&){ nav.pop(); };
|
||||
}
|
||||
|
||||
void SDCardDebugView::on_show() {
|
||||
sd_card_status_signal_token = sd_card::status_signal += [this](const sd_card::Status status) {
|
||||
this->on_status(status);
|
||||
};
|
||||
on_status(sd_card::status());
|
||||
}
|
||||
|
||||
void SDCardDebugView::on_hide() {
|
||||
sd_card::status_signal -= sd_card_status_signal_token;
|
||||
}
|
||||
|
||||
void SDCardDebugView::focus() {
|
||||
button_ok.focus();
|
||||
}
|
||||
|
||||
void SDCardDebugView::on_status(const sd_card::Status) {
|
||||
text_bus_width_value.set("");
|
||||
text_card_mode_value.set("");
|
||||
// text_csd_value.set("");
|
||||
text_block_size_value.set("");
|
||||
text_block_count_value.set("");
|
||||
text_capacity_value.set("");
|
||||
text_test_write_time_value.set("");
|
||||
text_test_write_rate_value.set("");
|
||||
text_test_read_time_value.set("");
|
||||
text_test_read_rate_value.set("");
|
||||
|
||||
const bool is_inserted = sdcIsCardInserted(&SDCD1);
|
||||
text_detected_value.set(is_inserted ? "Yes" : " No");
|
||||
|
||||
if( is_inserted ) {
|
||||
const auto card_width_flags = LPC_SDMMC->CTYPE & 0x10001;
|
||||
size_t card_width = 0;
|
||||
switch(card_width_flags) {
|
||||
case 0x00000: card_width = 1; break;
|
||||
case 0x00001: card_width = 4; break;
|
||||
case 0x10001: card_width = 8; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
text_bus_width_value.set(card_width ? to_string_dec_uint(card_width, 1) : "X");
|
||||
text_card_mode_value.set("0x" + to_string_hex(SDCD1.cardmode, 8));
|
||||
// text_csd_value.set("0x" + to_string_hex(SDCD1.csd, 8));
|
||||
|
||||
BlockDeviceInfo block_device_info;
|
||||
if( sdcGetInfo(&SDCD1, &block_device_info) == CH_SUCCESS ) {
|
||||
text_block_size_value.set(to_string_dec_uint(block_device_info.blk_size, 5));
|
||||
text_block_count_value.set(to_string_dec_uint(block_device_info.blk_num, 9));
|
||||
const uint64_t capacity = block_device_info.blk_size * uint64_t(block_device_info.blk_num);
|
||||
if( capacity >= 1000000000 ) {
|
||||
const uint32_t capacity_mb = capacity / 1000000U;
|
||||
const uint32_t fraction_gb = capacity_mb % 1000;
|
||||
const uint32_t capacity_gb = capacity_mb / 1000U;
|
||||
text_capacity_value.set(
|
||||
to_string_dec_uint(capacity_gb, 3) + "." +
|
||||
to_string_dec_uint(fraction_gb, 3, '0') + " GB"
|
||||
);
|
||||
} else {
|
||||
const uint32_t capacity_kb = capacity / 1000U;
|
||||
const uint32_t fraction_mb = capacity_kb % 1000;
|
||||
const uint32_t capacity_mb = capacity_kb / 1000U;
|
||||
text_capacity_value.set(
|
||||
to_string_dec_uint(capacity_mb, 3) + "." +
|
||||
to_string_dec_uint(fraction_mb, 3, '0') + " MB"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::string format_ticks_as_ms(const halrtcnt_t value) {
|
||||
const uint32_t us = uint64_t(value) * 1000000U / halGetCounterFrequency();
|
||||
const uint32_t ms_frac = us % 1000U;
|
||||
const uint32_t ms_int = us / 1000U;
|
||||
if( ms_int < 1000 ) {
|
||||
return to_string_dec_uint(ms_int, 3) + "." + to_string_dec_uint(ms_frac, 3, '0');
|
||||
} else {
|
||||
return "HHH.HHH";
|
||||
}
|
||||
}
|
||||
|
||||
static std::string format_bytes_per_ticks_as_mib(const size_t bytes, const halrtcnt_t ticks) {
|
||||
const uint32_t bps = uint64_t(bytes) * halGetCounterFrequency() / ticks;
|
||||
const uint32_t kbps = bps / 1000U;
|
||||
const uint32_t mbps_frac = kbps % 1000U;
|
||||
const uint32_t mbps_int = kbps / 1000U;
|
||||
if( mbps_int < 1000 ) {
|
||||
return to_string_dec_uint(mbps_int, 3) + "." + to_string_dec_uint(mbps_frac, 3, '0');
|
||||
} else {
|
||||
return "HHH.HHH";
|
||||
}
|
||||
}
|
||||
|
||||
void SDCardDebugView::on_test() {
|
||||
text_test_write_time_value.set("");
|
||||
text_test_write_rate_value.set("");
|
||||
text_test_read_time_value.set("");
|
||||
text_test_read_rate_value.set("");
|
||||
|
||||
SDCardTestThread thread;
|
||||
|
||||
// uint32_t spinner_phase = 0;
|
||||
while( thread.result() == SDCardTestThread::Result::Incomplete ) {
|
||||
chThdSleepMilliseconds(100);
|
||||
|
||||
// spinner_phase += 1;
|
||||
// char c = '*';
|
||||
// switch(spinner_phase % 4) {
|
||||
// case 0: c = '-'; break;
|
||||
// case 1: c = '\\'; break;
|
||||
// case 2: c = '|'; break;
|
||||
// case 3: c = '/'; break;
|
||||
// default: c = '*'; break;
|
||||
// }
|
||||
// text_test_write_value.set({ c });
|
||||
}
|
||||
|
||||
if( thread.result() == SDCardTestThread::Result::OK ) {
|
||||
const auto stats = thread.stats();
|
||||
const auto write_duration_avg = stats.write_test_duration / stats.write_count;
|
||||
|
||||
text_test_write_time_value.set(
|
||||
format_ticks_as_ms(stats.write_duration_min) + "/" +
|
||||
format_ticks_as_ms(write_duration_avg) + "/" +
|
||||
format_ticks_as_ms(stats.write_duration_max)
|
||||
);
|
||||
|
||||
text_test_write_rate_value.set(
|
||||
format_bytes_per_ticks_as_mib(stats.write_bytes, stats.write_duration_min * stats.write_count) + " " +
|
||||
format_bytes_per_ticks_as_mib(stats.write_bytes, stats.write_test_duration)
|
||||
);
|
||||
|
||||
const auto read_duration_avg = stats.read_test_duration / stats.read_count;
|
||||
|
||||
text_test_read_time_value.set(
|
||||
format_ticks_as_ms(stats.read_duration_min) + "/" +
|
||||
format_ticks_as_ms(read_duration_avg) + "/" +
|
||||
format_ticks_as_ms(stats.read_duration_max)
|
||||
);
|
||||
|
||||
text_test_read_rate_value.set(
|
||||
format_bytes_per_ticks_as_mib(stats.read_bytes, stats.read_duration_min * stats.read_count) + " " +
|
||||
format_bytes_per_ticks_as_mib(stats.read_bytes, stats.read_test_duration)
|
||||
);
|
||||
} else {
|
||||
text_test_write_time_value.set("Fail: " + to_string_dec_int(toUType(thread.result()), 4));
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
203
firmware/application/ui_sd_card_debug.hpp
Normal file
203
firmware/application/ui_sd_card_debug.hpp
Normal file
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __UI_SD_CARD_DEBUG_H__
|
||||
#define __UI_SD_CARD_DEBUG_H__
|
||||
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
|
||||
#include "sd_card.hpp"
|
||||
|
||||
namespace ui {
|
||||
|
||||
class SDCardDebugView : public View {
|
||||
public:
|
||||
SDCardDebugView(NavigationView& nav);
|
||||
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
SignalToken sd_card_status_signal_token;
|
||||
|
||||
void on_status(const sd_card::Status status);
|
||||
void on_test();
|
||||
|
||||
Text text_title {
|
||||
{ (240 - (7 * 8)) / 2, 1 * 16, (7 * 8), 16 },
|
||||
"SD Card",
|
||||
};
|
||||
|
||||
static constexpr size_t detected_characters = 3;
|
||||
|
||||
Text text_detected_title {
|
||||
{ 0, 3 * 16, (8 * 8), 16 },
|
||||
"Detected",
|
||||
};
|
||||
|
||||
Text text_detected_value {
|
||||
{ 240 - (detected_characters * 8), 3 * 16, (detected_characters * 8), 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
static constexpr size_t bus_width_characters = 1;
|
||||
|
||||
Text text_bus_width_title {
|
||||
{ 0, 5 * 16, (9 * 8), 16 },
|
||||
"Bus width",
|
||||
};
|
||||
|
||||
Text text_bus_width_value {
|
||||
{ 240 - (bus_width_characters * 8), 5 * 16, (bus_width_characters * 8), 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
static constexpr size_t card_mode_characters = 10;
|
||||
|
||||
Text text_card_mode_title {
|
||||
{ 0, 6 * 16, (9 * 8), 16 },
|
||||
"Card mode",
|
||||
};
|
||||
|
||||
Text text_card_mode_value {
|
||||
{ 240 - (card_mode_characters * 8), 6 * 16, (card_mode_characters * 8), 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
// static constexpr size_t csd_characters = 10;
|
||||
|
||||
// Text text_csd_title {
|
||||
// { 0, 7 * 16, (3 * 8), 16 },
|
||||
// "CSD",
|
||||
// };
|
||||
|
||||
// Text text_csd_value {
|
||||
// { 240 - (csd_characters * 8), 7 * 16, (csd_characters * 8), 16 },
|
||||
// "",
|
||||
// };
|
||||
|
||||
static constexpr size_t block_size_characters = 5;
|
||||
|
||||
Text text_block_size_title {
|
||||
{ 0, 8 * 16, (10 * 8), 16 },
|
||||
"Block size",
|
||||
};
|
||||
|
||||
Text text_block_size_value {
|
||||
{ 240 - (block_size_characters * 8), 8 * 16, (block_size_characters * 8), 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
static constexpr size_t block_count_characters = 9;
|
||||
|
||||
Text text_block_count_title {
|
||||
{ 0, 9 * 16, (11 * 8), 16 },
|
||||
"Block count",
|
||||
};
|
||||
|
||||
Text text_block_count_value {
|
||||
{ 240 - (block_count_characters * 8), 9 * 16, (block_count_characters * 8), 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
static constexpr size_t capacity_characters = 10;
|
||||
|
||||
Text text_capacity_title {
|
||||
{ 0, 10 * 16, (8 * 8), 16 },
|
||||
"Capacity",
|
||||
};
|
||||
|
||||
Text text_capacity_value {
|
||||
{ 240 - (capacity_characters * 8), 10 * 16, (capacity_characters * 8), 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
static constexpr size_t test_write_time_characters = 23;
|
||||
|
||||
Text text_test_write_time_title {
|
||||
{ 0, 12 * 16, (4 * 8), 16 },
|
||||
"W ms",
|
||||
};
|
||||
|
||||
Text text_test_write_time_value {
|
||||
{ 240 - (test_write_time_characters * 8), 12 * 16, (test_write_time_characters * 8), 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
static constexpr size_t test_write_rate_characters = 23;
|
||||
|
||||
Text text_test_write_rate_title {
|
||||
{ 0, 13 * 16, (6 * 8), 16 },
|
||||
"W MB/s",
|
||||
};
|
||||
|
||||
Text text_test_write_rate_value {
|
||||
{ 240 - (test_write_rate_characters * 8), 13 * 16, (test_write_rate_characters * 8), 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
static constexpr size_t test_read_time_characters = 23;
|
||||
|
||||
Text text_test_read_time_title {
|
||||
{ 0, 14 * 16, (4 * 8), 16 },
|
||||
"R ms",
|
||||
};
|
||||
|
||||
Text text_test_read_time_value {
|
||||
{ 240 - (test_read_time_characters * 8), 14 * 16, (test_read_time_characters * 8), 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
static constexpr size_t test_read_rate_characters = 23;
|
||||
|
||||
Text text_test_read_rate_title {
|
||||
{ 0, 15 * 16, (6 * 8), 16 },
|
||||
"R MB/s",
|
||||
};
|
||||
|
||||
Text text_test_read_rate_value {
|
||||
{ 240 - (test_read_rate_characters * 8), 15 * 16, (test_read_rate_characters * 8), 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
Button button_test {
|
||||
{ 16, 17 * 16, 96, 24 },
|
||||
"Test"
|
||||
};
|
||||
|
||||
Button button_ok {
|
||||
{ 240 - 96 - 16, 17 * 16, 96, 24 },
|
||||
"OK"
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_SD_CARD_DEBUG_H__*/
|
|
@ -54,8 +54,8 @@ static constexpr Bitmap bitmap_sd_card_unknown {
|
|||
|
||||
static constexpr uint8_t bitmap_sd_card_error_data[] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f,
|
||||
0xf8, 0x1f, 0xf8, 0x1b, 0xf8, 0x19, 0xf8, 0x1c,
|
||||
0xf8, 0x1e, 0xf8, 0x1c, 0xf8, 0x19, 0xf8, 0x1b,
|
||||
0xf8, 0x1f, 0xd8, 0x1b, 0x98, 0x19, 0x38, 0x1c,
|
||||
0x78, 0x1e, 0x38, 0x1c, 0x98, 0x19, 0xd8, 0x1b,
|
||||
0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ void SetDateTimeView::focus() {
|
|||
button_cancel.focus();
|
||||
}
|
||||
|
||||
void SetDateTimeView::form_init(const SetDateTimeModel model) {
|
||||
void SetDateTimeView::form_init(const SetDateTimeModel& model) {
|
||||
field_year.set_value(model.year);
|
||||
field_month.set_value(model.month);
|
||||
field_day.set_value(model.day);
|
||||
|
@ -137,7 +137,7 @@ void SetFrequencyCorrectionView::focus() {
|
|||
button_cancel.focus();
|
||||
}
|
||||
|
||||
void SetFrequencyCorrectionView::form_init(const SetFrequencyCorrectionModel model) {
|
||||
void SetFrequencyCorrectionView::form_init(const SetFrequencyCorrectionModel& model) {
|
||||
field_ppm.set_value(model.ppm);
|
||||
}
|
||||
|
||||
|
|
|
@ -42,9 +42,6 @@ struct SetDateTimeModel {
|
|||
|
||||
class SetDateTimeView : public View {
|
||||
public:
|
||||
std::function<void(SetDateTimeModel)> on_ok;
|
||||
std::function<void()> on_cancel;
|
||||
|
||||
SetDateTimeView(NavigationView& nav);
|
||||
|
||||
void focus() override;
|
||||
|
@ -129,7 +126,7 @@ private:
|
|||
"Cancel",
|
||||
};
|
||||
|
||||
void form_init(const SetDateTimeModel model);
|
||||
void form_init(const SetDateTimeModel& model);
|
||||
SetDateTimeModel form_collect();
|
||||
};
|
||||
|
||||
|
@ -139,9 +136,6 @@ struct SetFrequencyCorrectionModel {
|
|||
|
||||
class SetFrequencyCorrectionView : public View {
|
||||
public:
|
||||
std::function<void(SetFrequencyCorrectionModel)> on_ok;
|
||||
std::function<void()> on_cancel;
|
||||
|
||||
SetFrequencyCorrectionView(NavigationView& nav);
|
||||
|
||||
void focus() override;
|
||||
|
@ -173,7 +167,7 @@ private:
|
|||
"Cancel",
|
||||
};
|
||||
|
||||
void form_init(const SetFrequencyCorrectionModel model);
|
||||
void form_init(const SetFrequencyCorrectionModel& model);
|
||||
SetFrequencyCorrectionModel form_collect();
|
||||
};
|
||||
|
||||
|
|
|
@ -26,9 +26,10 @@
|
|||
#include "spectrum_color_lut.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
@ -251,19 +252,11 @@ void WaterfallWidget::on_show() {
|
|||
}
|
||||
);
|
||||
|
||||
shared_memory.baseband_queue.push_and_wait(
|
||||
SpectrumStreamingConfigMessage {
|
||||
SpectrumStreamingConfigMessage::Mode::Running
|
||||
}
|
||||
);
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void WaterfallWidget::on_hide() {
|
||||
shared_memory.baseband_queue.push_and_wait(
|
||||
SpectrumStreamingConfigMessage {
|
||||
SpectrumStreamingConfigMessage::Mode::Stopped
|
||||
}
|
||||
);
|
||||
baseband::spectrum_streaming_stop();
|
||||
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync);
|
||||
EventDispatcher::message_map().unregister_handler(Message::ID::ChannelSpectrumConfig);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue