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:
furrtek 2016-04-21 20:36:19 +02:00
commit 1b0da68d65
252 changed files with 10752 additions and 7183 deletions

View File

@ -2,12 +2,21 @@ language: cpp
cache: apt cache: apt
before_install: notifications:
- sudo add-apt-repository ppa:terry.guo/gcc-arm-embedded -y irc:
- sudo apt-get update -qq channels:
- sudo apt-get install -y gcc-arm-none-eabi - "chat.freenode.net#portapack"
template:
- "%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}"
- "Change view : %{compare_url}"
- "Build details : %{build_url}"
# TODO: The "build_number.1" in this URL is almost certainly wrong, but correct value not available from Travis?
- "Firmware download : https://portapack-h1-builds.s3.amazonaws.com/%{repository_slug}/%{build_number}/%{build_number}.1/firmware/portapack-h1-firmware-%{commit}.tar.bz2"
before_script: before_script:
- wget https://launchpad.net/gcc-arm-embedded/5.0/5-2015-q4-major/+download/gcc-arm-none-eabi-5_2-2015q4-20151219-linux.tar.bz2 -O /tmp/gcc-arm.tar.bz2
- tar -xf /tmp/gcc-arm.tar.bz2
- export PATH=$PWD/gcc-arm-none-eabi-5_2-2015q4/bin:$PATH
- export CC="arm-none-eabi-gcc" - export CC="arm-none-eabi-gcc"
- export CXX="arm-none-eabi-g++" - export CXX="arm-none-eabi-g++"
@ -18,6 +27,10 @@ script:
- popd - popd
addons: addons:
apt_packages:
- lib32bz2-1.0
- lib32ncurses5
- lib32z1
artifacts: artifacts:
paths: paths:
- $(ls firmware/portapack-h1-firmware-*.tar.bz2 | tr "\n" ":") - $(ls firmware/portapack-h1-firmware-*.tar.bz2 | tr "\n" ":")

View File

@ -127,6 +127,7 @@ CPPSRC = main.cpp \
hackrf_hal.cpp \ hackrf_hal.cpp \
portapack.cpp \ portapack.cpp \
portapack_shared_memory.cpp \ portapack_shared_memory.cpp \
baseband_api.cpp \
portapack_persistent_memory.cpp \ portapack_persistent_memory.cpp \
portapack_io.cpp \ portapack_io.cpp \
i2c_pp.cpp \ i2c_pp.cpp \
@ -146,6 +147,7 @@ CPPSRC = main.cpp \
touch.cpp \ touch.cpp \
touch_adc.cpp \ touch_adc.cpp \
encoder.cpp \ encoder.cpp \
audio.cpp \
lcd_ili9341.cpp \ lcd_ili9341.cpp \
ui.cpp \ ui.cpp \
ui_alphanum.cpp \ ui_alphanum.cpp \
@ -167,6 +169,7 @@ CPPSRC = main.cpp \
ui_debug.cpp \ ui_debug.cpp \
ui_baseband_stats_view.cpp \ ui_baseband_stats_view.cpp \
ui_sd_card_status_view.cpp \ ui_sd_card_status_view.cpp \
ui_sd_card_debug.cpp \
ui_console.cpp \ ui_console.cpp \
ui_receiver.cpp \ ui_receiver.cpp \
ui_spectrum.cpp \ ui_spectrum.cpp \
@ -185,12 +188,15 @@ CPPSRC = main.cpp \
../commom/ais_packet.cpp \ ../commom/ais_packet.cpp \
ais_app.cpp \ ais_app.cpp \
tpms_app.cpp \ tpms_app.cpp \
../common/tpms_packet.cpp \
ert_app.cpp \ ert_app.cpp \
../common/ert_packet.cpp \ ../common/ert_packet.cpp \
spectrum_analysis_app.cpp \ capture_app.cpp \
sd_card.cpp \ sd_card.cpp \
file.cpp \ file.cpp \
log_file.cpp \ log_file.cpp \
png_writer.cpp \
audio_thread.cpp \
manchester.cpp \ manchester.cpp \
string_format.cpp \ string_format.cpp \
temperature_logger.cpp \ temperature_logger.cpp \
@ -198,7 +204,8 @@ CPPSRC = main.cpp \
../common/chibios_cpp.cpp \ ../common/chibios_cpp.cpp \
../common/debug.cpp \ ../common/debug.cpp \
../common/gcc.cpp \ ../common/gcc.cpp \
m4_startup.cpp \ ../common/lfsr_random.cpp \
core_control.cpp \
cpld_max5.cpp \ cpld_max5.cpp \
jtag.cpp \ jtag.cpp \
cpld_update.cpp \ cpld_update.cpp \

View File

@ -25,8 +25,7 @@
#include "string_format.hpp" #include "string_format.hpp"
#include "portapack.hpp" #include "baseband_api.hpp"
using namespace portapack;
#include <algorithm> #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; 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) { static std::string latlon(const Latitude latitude, const Longitude longitude) {
if( latitude.is_valid() && longitude.is_valid() ) { if( latitude.is_valid() && longitude.is_valid() ) {
return latlon_abs_normalized(latitude.normalized(), "SN") + " " + latlon_abs_normalized(longitude.normalized(), "WE"); 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); 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) { static std::string navigational_status(const unsigned int value) {
switch(value) { switch(value) {
case 0: return "under way w/engine"; case 0: return "under way w/engine";
@ -160,6 +128,12 @@ static std::string true_heading(const TrueHeading value) {
} /* namespace format */ } /* namespace format */
} /* namespace ais */ } /* namespace ais */
AISLogger::AISLogger(
const std::string& file_path
) : log_file { file_path }
{
}
void AISLogger::on_packet(const ais::Packet& packet) { void AISLogger::on_packet(const ais::Packet& packet) {
// TODO: Unstuff here, not in baseband! // TODO: Unstuff here, not in baseband!
if( log_file.is_ready() ) { 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) { options_channel.on_change = [this](size_t, OptionsField::value_t v) {
this->on_frequency_changed(v); this->on_frequency_changed(v);
}; };
options_channel.set_by_value(162025000); options_channel.set_by_value(target_frequency());
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();
recent_entries_view.on_select = [this](const AISRecentEntry& entry) { recent_entries_view.on_select = [this](const AISRecentEntry& entry) {
this->on_show_detail(entry); this->on_show_detail(entry);
@ -363,10 +343,14 @@ AISAppView::AISAppView(NavigationView&) {
recent_entry_detail_view.on_close = [this]() { recent_entry_detail_view.on_close = [this]() {
this->on_show_list(); this->on_show_list();
}; };
logger = std::make_unique<AISLogger>("ais.txt");
} }
AISAppView::~AISAppView() { AISAppView::~AISAppView() {
receiver_model.disable(); baseband::stop();
radio::disable();
EventDispatcher::message_map().unregister_handler(Message::ID::AISPacket); 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) { 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); const auto updated_entry = recent.on_packet(packet.source_id(), packet);
recent_entries_view.set_dirty(); recent_entries_view.set_dirty();
@ -405,8 +392,21 @@ void AISAppView::on_show_detail(const AISRecentEntry& entry) {
recent_entry_detail_view.focus(); recent_entry_detail_view.focus();
} }
void AISAppView::on_frequency_changed(const uint32_t new_frequency) { void AISAppView::on_frequency_changed(const uint32_t new_target_frequency) {
receiver_model.set_tuning_frequency(new_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 */ } /* namespace ui */

View File

@ -90,10 +90,12 @@ using AISRecentEntries = RecentEntries<ais::Packet, AISRecentEntry>;
class AISLogger { class AISLogger {
public: public:
AISLogger(const std::string& file_path);
void on_packet(const ais::Packet& packet); void on_packet(const ais::Packet& packet);
private: private:
LogFile log_file { "ais.txt" }; LogFile log_file;
}; };
namespace ui { namespace ui {
@ -145,8 +147,12 @@ public:
std::string title() const override { return "AIS"; }; std::string title() const override { return "AIS"; };
private: 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; AISRecentEntries recent;
AISLogger logger; std::unique_ptr<AISLogger> logger;
AISRecentEntriesView recent_entries_view { recent }; AISRecentEntriesView recent_entries_view { recent };
AISRecentEntryDetailView recent_entry_detail_view; 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_packet(const ais::Packet& packet);
void on_show_list(); void on_show_list();
void on_show_detail(const AISRecentEntry& entry); 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 */ } /* namespace ui */

View File

@ -22,9 +22,12 @@
#include "analog_audio_app.hpp" #include "analog_audio_app.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "portapack_shared_memory.hpp" #include "portapack_persistent_memory.hpp"
using namespace portapack; using namespace portapack;
#include "audio.hpp"
#include "file.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace ui { namespace ui {
@ -115,6 +118,9 @@ AnalogAudioView::AnalogAudioView(
field_vga.on_change = [this](int32_t v_db) { field_vga.on_change = [this](int32_t v_db) {
this->on_vga_changed(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(); const auto modulation = receiver_model.modulation();
options_modulation.set_by_value(modulation); options_modulation.set_by_value(modulation);
@ -125,18 +131,20 @@ AnalogAudioView::AnalogAudioView(
this->on_show_options_modulation(); 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) { field_volume.on_change = [this](int32_t v) {
this->on_headphone_volume_changed(v); this->on_headphone_volume_changed(v);
}; };
audio::output::start();
update_modulation(static_cast<ReceiverModel::Mode>(modulation)); update_modulation(static_cast<ReceiverModel::Mode>(modulation));
} }
AnalogAudioView::~AnalogAudioView() { AnalogAudioView::~AnalogAudioView() {
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do // TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
// both? // both?
audio_codec.headphone_mute(); audio::output::stop();
receiver_model.disable(); receiver_model.disable();
} }
@ -212,16 +220,13 @@ void AnalogAudioView::on_show_options_frequency() {
field_frequency.set_style(&style_options_group); field_frequency.set_style(&style_options_group);
auto widget = std::make_unique<FrequencyOptionsView>( auto widget = std::make_unique<FrequencyOptionsView>(options_view_rect, &style_options_group);
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
&style_options_group
);
widget->set_step(receiver_model.frequency_step()); widget->set_step(receiver_model.frequency_step());
widget->on_change_step = [this](rf::Frequency f) { widget->on_change_step = [this](rf::Frequency f) {
this->on_frequency_step_changed(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) { widget->on_change_reference_ppm_correction = [this](int32_t v) {
this->on_reference_ppm_correction_changed(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); field_lna.set_style(&style_options_group);
auto widget = std::make_unique<RadioGainOptionsView>( auto widget = std::make_unique<RadioGainOptionsView>(options_view_rect, &style_options_group);
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
&style_options_group
);
widget->set_rf_amp(receiver_model.rf_amp()); widget->set_rf_amp(receiver_model.rf_amp());
widget->on_change_rf_amp = [this](bool enable) { 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()); const auto modulation = static_cast<ReceiverModel::Mode>(receiver_model.modulation());
if( modulation == ReceiverModel::Mode::AMAudio ) { if( modulation == ReceiverModel::Mode::AMAudio ) {
options_modulation.set_style(&style_options_group); options_modulation.set_style(&style_options_group);
auto widget = std::make_unique<AMOptionsView>( auto widget = std::make_unique<AMOptionsView>(options_view_rect, &style_options_group);
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
&style_options_group
);
set_options_widget(std::move(widget)); set_options_widget(std::move(widget));
} }
if( modulation == ReceiverModel::Mode::NarrowbandFMAudio ) { if( modulation == ReceiverModel::Mode::NarrowbandFMAudio ) {
options_modulation.set_style(&style_options_group); options_modulation.set_style(&style_options_group);
auto widget = std::make_unique<NBFMOptionsView>( auto widget = std::make_unique<NBFMOptionsView>(options_view_rect, &style_options_group);
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
&style_options_group
);
set_options_widget(std::move(widget)); 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) { 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) { 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); receiver_model.set_headphone_volume(new_volume);
} }
void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) { void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
audio::output::mute();
audio_thread.reset();
const auto is_wideband_spectrum_mode = (modulation == ReceiverModel::Mode::SpectrumAnalysis); const auto is_wideband_spectrum_mode = (modulation == ReceiverModel::Mode::SpectrumAnalysis);
receiver_model.set_baseband_configuration({ receiver_model.set_baseband_configuration({
.mode = toUType(modulation), .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.set_baseband_bandwidth(is_wideband_spectrum_mode ? 12000000 : 1750000);
receiver_model.enable(); 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 */ } /* namespace ui */

View File

@ -27,6 +27,8 @@
#include "ui_receiver.hpp" #include "ui_receiver.hpp"
#include "ui_spectrum.hpp" #include "ui_spectrum.hpp"
#include "audio_thread.hpp"
#include "ui_font_fixed_8x16.hpp" #include "ui_font_fixed_8x16.hpp"
namespace ui { namespace ui {
@ -93,6 +95,8 @@ public:
private: private:
static constexpr ui::Dim header_height = 2 * 16; static constexpr ui::Dim header_height = 2 * 16;
const Rect options_view_rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 };
RSSI rssi { RSSI rssi {
{ 21 * 8, 0, 6 * 8, 4 }, { 21 * 8, 0, 6 * 8, 4 },
}; };
@ -113,12 +117,8 @@ private:
{ 15 * 8, 0 * 16 } { 15 * 8, 0 * 16 }
}; };
NumberField field_vga { VGAGainField field_vga {
{ 18 * 8, 0 * 16}, { 18 * 8, 0 * 16 }
2,
{ max2837::vga::gain_db_range.minimum, max2837::vga::gain_db_range.maximum },
max2837::vga::gain_db_step,
' ',
}; };
OptionsField options_modulation { OptionsField options_modulation {
@ -144,6 +144,8 @@ private:
spectrum::WaterfallWidget waterfall; spectrum::WaterfallWidget waterfall;
std::unique_ptr<AudioThread> audio_thread;
void on_tuning_frequency_changed(rf::Frequency f); void on_tuning_frequency_changed(rf::Frequency f);
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz); void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
void on_rf_amp_changed(bool v); void on_rf_amp_changed(bool v);

View File

@ -19,27 +19,21 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#ifndef __AUDIO_H__ #include "audio.hpp"
#define __AUDIO_H__
#include "buffer.hpp" #include "portapack.hpp"
using portapack::i2c0;
using portapack::clock_manager;
#include "wm8731.hpp"
using wolfson::wm8731::WM8731;
#include "i2s.hpp" #include "i2s.hpp"
using namespace lpc43xx; using namespace lpc43xx;
namespace audio { namespace audio {
struct sample_t { namespace {
union {
struct {
int16_t left;
int16_t right;
};
uint32_t raw;
};
};
using buffer_t = buffer_t<sample_t>;
constexpr i2s::ConfigTX i2s0_config_tx { constexpr i2s::ConfigTX i2s0_config_tx {
.dao = i2s::DAO { .dao = i2s::DAO {
@ -103,6 +97,78 @@ constexpr i2s::ConfigDMA i2s0_config_dma {
}, },
}; };
} /* namespace audio */ constexpr uint8_t wm8731_i2c_address = 0x1a;
#endif/*__AUDIO_H__*/ 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 */

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

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. * Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* *
* This file is part of PortaPack. * This file is part of PortaPack.
* *
@ -19,8 +19,6 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#include "audio.hpp" #include "audio_thread.hpp"
namespace audio { Thread* AudioThread::thread = nullptr;
} /* namespace audio */

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

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

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

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

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

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

View File

@ -19,8 +19,8 @@
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
#ifndef __M4_STARTUP_H__ #ifndef __CORE_CONTROL_H__
#define __M4_STARTUP_H__ #define __CORE_CONTROL_H__
#include <cstddef> #include <cstddef>
@ -34,4 +34,6 @@ void m4_request_shutdown();
void m4_switch(const char * hash); void m4_switch(const char * hash);
int m4_load_image(void); int m4_load_image(void);
#endif/*__M4_STARTUP_H__*/ void m0_halt();
#endif/*__CORE_CONTROL_H__*/

View File

@ -23,8 +23,7 @@
#include "event_m0.hpp" #include "event_m0.hpp"
#include "portapack.hpp" #include "baseband_api.hpp"
using namespace portapack;
#include "manchester.hpp" #include "manchester.hpp"
@ -52,10 +51,20 @@ std::string consumption(Consumption value) {
return to_string_dec_uint(value, 10); return to_string_dec_uint(value, 10);
} }
std::string commodity_type(CommodityType value) {
return to_string_dec_uint(value, 2);
}
} /* namespace format */ } /* namespace format */
} /* namespace ert */ } /* namespace ert */
ERTLogger::ERTLogger(
const std::string& file_path
) : log_file { file_path }
{
}
void ERTLogger::on_packet(const ert::Packet& packet) { void ERTLogger::on_packet(const ert::Packet& packet) {
if( log_file.is_ready() ) { if( log_file.is_ready() ) {
const auto formatted = packet.symbols_formatted(); 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) { void ERTRecentEntry::update(const ert::Packet& packet) {
received_count++; received_count++;
@ -71,8 +82,9 @@ void ERTRecentEntry::update(const ert::Packet& packet) {
namespace ui { 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 }, { "ID", 10 },
{ "Tp", 2 },
{ "Consumpt", 10 }, { "Consumpt", 10 },
{ "Cnt", 3 }, { "Cnt", 3 },
} }; } };
@ -106,7 +118,7 @@ void RecentEntriesView<ERTRecentEntries>::draw(
) { ) {
const auto& draw_style = is_selected ? style.invert() : style; 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 ) { if( entry.received_count > 999 ) {
line += " +++"; 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, .mode = 6,
.sampling_rate = 4194304, .sampling_rate = sampling_rate,
.decimation_factor = 1, .decimation_factor = 1,
}); });
receiver_model.set_baseband_bandwidth(2500000);
receiver_model.set_rf_amp(false); logger = std::make_unique<ERTLogger>("ert.txt");
receiver_model.set_lna(32);
receiver_model.set_vga(32);
receiver_model.set_tuning_frequency(911600000);
receiver_model.enable();
} }
ERTAppView::~ERTAppView() { ERTAppView::~ERTAppView() {
receiver_model.disable(); baseband::stop();
radio::disable();
EventDispatcher::message_map().unregister_handler(Message::ID::ERTPacket); 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) { void ERTAppView::on_packet(const ert::Packet& packet) {
logger.on_packet(packet); if( logger ) {
logger->on_packet(packet);
}
if( packet.crc_ok() ) { if( packet.crc_ok() ) {
recent.on_packet(packet.id(), packet); recent.on_packet({ packet.id(), packet.commodity_type() }, packet);
recent_entries_view.set_dirty(); recent_entries_view.set_dirty();
} }
} }

View File

@ -33,13 +33,37 @@
#include <cstddef> #include <cstddef>
#include <string> #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 { struct ERTRecentEntry {
using Key = ert::ID; using Key = ERTKey;
// TODO: Is this the right choice of invalid key value? // 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 }; size_t received_count { 0 };
@ -47,12 +71,13 @@ struct ERTRecentEntry {
ERTRecentEntry( ERTRecentEntry(
const Key& key const Key& key
) : id { key } ) : id { key.id },
commodity_type { key.commodity_type }
{ {
} }
Key key() const { Key key() const {
return id; return { id, commodity_type };
} }
void update(const ert::Packet& packet); void update(const ert::Packet& packet);
@ -60,10 +85,12 @@ struct ERTRecentEntry {
class ERTLogger { class ERTLogger {
public: public:
ERTLogger(const std::string& file_path);
void on_packet(const ert::Packet& packet); void on_packet(const ert::Packet& packet);
private: private:
LogFile log_file { "ert.txt" }; LogFile log_file;
}; };
using ERTRecentEntries = RecentEntries<ert::Packet, ERTRecentEntry>; using ERTRecentEntries = RecentEntries<ert::Packet, ERTRecentEntry>;
@ -74,6 +101,10 @@ using ERTRecentEntriesView = RecentEntriesView<ERTRecentEntries>;
class ERTAppView : public View { class ERTAppView : public View {
public: 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(NavigationView& nav);
~ERTAppView(); ~ERTAppView();
@ -89,7 +120,7 @@ public:
private: private:
ERTRecentEntries recent; ERTRecentEntries recent;
ERTLogger logger; std::unique_ptr<ERTLogger> logger;
ERTRecentEntriesView recent_entries_view { recent }; ERTRecentEntriesView recent_entries_view { recent };

View File

@ -31,6 +31,8 @@
#include "irq_controls.hpp" #include "irq_controls.hpp"
#include "audio_thread.hpp"
#include "ch.h" #include "ch.h"
#include "lpc43xx_cpp.hpp" #include "lpc43xx_cpp.hpp"
@ -44,6 +46,7 @@ CH_IRQ_HANDLER(M4Core_IRQHandler) {
CH_IRQ_PROLOGUE(); CH_IRQ_PROLOGUE();
chSysLockFromIsr(); chSysLockFromIsr();
AudioThread::check_fifo_isr();
EventDispatcher::events_flag_isr(EVT_MASK_APPLICATION); EventDispatcher::events_flag_isr(EVT_MASK_APPLICATION);
chSysUnlockFromIsr(); chSysUnlockFromIsr();
@ -56,6 +59,7 @@ CH_IRQ_HANDLER(M4Core_IRQHandler) {
MessageHandlerMap EventDispatcher::message_map_; MessageHandlerMap EventDispatcher::message_map_;
Thread* EventDispatcher::thread_event_loop = nullptr; Thread* EventDispatcher::thread_event_loop = nullptr;
Thread* EventDispatcher::thread_record = nullptr;
EventDispatcher::EventDispatcher( EventDispatcher::EventDispatcher(
ui::Widget* const top_widget, ui::Widget* const top_widget,
@ -65,6 +69,8 @@ EventDispatcher::EventDispatcher(
painter(painter), painter(painter),
context(context) context(context)
{ {
init_message_queues();
thread_event_loop = chThdSelf(); thread_event_loop = chThdSelf();
touch_manager.on_event = [this](const ui::TouchEvent event) { touch_manager.on_event = [this](const ui::TouchEvent event) {
this->on_touch_event(event); this->on_touch_event(event);
@ -132,10 +138,9 @@ void EventDispatcher::dispatch(const eventmask_t events) {
} }
void EventDispatcher::handle_application_queue() { void EventDispatcher::handle_application_queue() {
std::array<uint8_t, Message::MAX_SIZE> message_buffer; shared_memory.application_queue.handle([](Message* const message) {
while(Message* const message = shared_memory.application_queue.pop(message_buffer)) {
message_map().send(message); message_map().send(message);
} });
} }
void EventDispatcher::handle_rtc_tick() { 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) ) { while( (target != nullptr) && !target->on_encoder(event) ) {
target = target->parent(); 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;
}

View File

@ -73,6 +73,8 @@ public:
return message_map_; return message_map_;
} }
static Thread* thread_record;
private: private:
static MessageHandlerMap message_map_; static MessageHandlerMap message_map_;
static Thread* thread_event_loop; static Thread* thread_event_loop;
@ -105,6 +107,8 @@ private:
bool event_bubble_key(const ui::KeyEvent event); bool event_bubble_key(const ui::KeyEvent event);
void event_bubble_encoder(const ui::EncoderEvent event); void event_bubble_encoder(const ui::EncoderEvent event);
void init_message_queues();
}; };
#endif/*__EVENT_M0_H__*/ #endif/*__EVENT_M0_H__*/

View File

@ -1,273 +1,279 @@
/* CHIBIOS FIX */ /* CHIBIOS FIX */
#include "ch.h" #include "ch.h"
/*---------------------------------------------------------------------------/ /*---------------------------------------------------------------------------/
/ FatFs - FAT file system module configuration file R0.10c (C)ChaN, 2014 / FatFs - FAT file system module configuration file R0.11a (C)ChaN, 2015
/---------------------------------------------------------------------------*/ /---------------------------------------------------------------------------*/
#define _FFCONF 80376 /* Revision ID */ #define _FFCONF 64180 /* Revision ID */
/*---------------------------------------------------------------------------/ /*---------------------------------------------------------------------------/
/ Functions and Buffer Configurations / Function Configurations
/---------------------------------------------------------------------------*/ /---------------------------------------------------------------------------*/
#define _FS_TINY 0 #define _FS_READONLY 0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) /* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS / Read-only configuration removes writing API functions, f_write(), f_sync(),
/ bytes. Instead of private sector buffer eliminated from the file object, / f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
/ common sector buffer in the file system object (FATFS) is used for the file / and optional writing functions as well. */
/ data transfer. */
#define _FS_MINIMIZE 0
#define _FS_READONLY 0 /* This option defines minimization level to remove some basic API functions.
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only) /
/ Read-only configuration removes basic writing API functions, f_write(), / 0: All basic functions are enabled.
/ f_sync(), f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), / 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
/ f_getfree() and optional writing functions as well. */ / 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 _FS_MINIMIZE 0
/* This option defines minimization level to remove some API functions.
/ #define _USE_STRFUNC 1
/ 0: All basic functions are enabled. /* This option switches string functions, f_gets(), f_putc(), f_puts() and
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(), / f_printf().
/ f_truncate() and f_rename() function are removed. /
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. / 0: Disable string functions.
/ 3: f_lseek() function is removed in addition to 2. */ / 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion. */
#define _USE_STRFUNC 1
/* This option switches string functions, f_gets(), f_putc(), f_puts() and #define _USE_FIND 1
/ f_printf(). /* This option switches filtered directory read feature and related functions,
/ / f_findfirst() and f_findnext(). (0:Disable or 1:Enable) */
/ 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) */
#define _USE_MKFS 0
/* This option switches f_mkfs() function. (0:Disable or 1:Enable) #define _USE_FASTSEEK 1
/ To enable it, also _FS_READONLY need to be set to 0. */ /* This option switches fast seek feature. (0:Disable or 1:Enable) */
#define _USE_FASTSEEK 0 #define _USE_LABEL 0
/* This option switches fast seek feature. (0:Disable or 1:Enable) */ /* This option switches volume label functions, f_getlabel() and f_setlabel().
/ (0:Disable or 1:Enable) */
#define _USE_LABEL 0
/* This option switches volume label functions, f_getlabel() and f_setlabel(). #define _USE_FORWARD 0
/ (0:Disable or 1:Enable) */ /* This option switches f_forward() function. (0:Disable or 1:Enable)
/ To enable it, also _FS_TINY need to be set to 1. */
#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
/ Locale and Namespace Configurations /* 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.
/
#define _CODE_PAGE 1252 / 1 - ASCII (No extended character. Non-LFN cfg. only)
/* This option specifies the OEM code page to be used on the target system. / 437 - U.S.
/ Incorrect setting of the code page can cause a file open failure. / 720 - Arabic
/ / 737 - Greek
/ 932 - Japanese Shift_JIS (DBCS, OEM, Windows) / 771 - KBL
/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows) / 775 - Baltic
/ 949 - Korean (DBCS, OEM, Windows) / 850 - Latin 1
/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows) / 852 - Latin 2
/ 1250 - Central Europe (Windows) / 855 - Cyrillic
/ 1251 - Cyrillic (Windows) / 857 - Turkish
/ 1252 - Latin 1 (Windows) / 860 - Portuguese
/ 1253 - Greek (Windows) / 861 - Icelandic
/ 1254 - Turkish (Windows) / 862 - Hebrew
/ 1255 - Hebrew (Windows) / 863 - Canadian French
/ 1256 - Arabic (Windows) / 864 - Arabic
/ 1257 - Baltic (Windows) / 865 - Nordic
/ 1258 - Vietnam (OEM, Windows) / 866 - Russian
/ 437 - U.S. (OEM) / 869 - Greek 2
/ 720 - Arabic (OEM) / 932 - Japanese (DBCS)
/ 737 - Greek (OEM) / 936 - Simplified Chinese (DBCS)
/ 775 - Baltic (OEM) / 949 - Korean (DBCS)
/ 850 - Multilingual Latin 1 (OEM) / 950 - Traditional Chinese (DBCS)
/ 858 - Multilingual Latin 1 + Euro (OEM) */
/ 852 - Latin 2 (OEM)
/ 855 - Cyrillic (OEM)
/ 866 - Russian (OEM) #define _USE_LFN 0
/ 857 - Turkish (OEM) #define _MAX_LFN 255
/ 862 - Hebrew (OEM) /* The _USE_LFN option switches the LFN feature.
/ 874 - Thai (OEM, Windows) /
/ 1 - ASCII (No extended character. Valid for only non-LFN configuration.) */ / 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.
#define _USE_LFN 0 / 3: Enable LFN with dynamic working buffer on the HEAP.
#define _MAX_LFN 255 /
/* The _USE_LFN option switches the LFN feature. / 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.
/ 0: Disable LFN feature. _MAX_LFN has no effect. / When use stack for the working buffer, take care on stack overflow. When use heap
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. / memory for the working buffer, memory management functions, ff_memalloc() and
/ 2: Enable LFN with dynamic working buffer on the STACK. / ff_memfree(), must be added to the project. */
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ When enable the LFN feature, Unicode handling functions (option/unicode.c) must #define _LFN_UNICODE 0
/ be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes. /* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode)
/ When use stack for the working buffer, take care on stack overflow. When use heap / To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE
/ memory for the working buffer, memory management functions, ff_memalloc() and / to 1. This option also affects behavior of string I/O functions. */
/ ff_memfree(), must be added to the project. */
#define _STRF_ENCODE 3
#define _LFN_UNICODE 0 /* When _LFN_UNICODE is 1, this option selects the character encoding on the file to
/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode) / be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
/ 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. */ / 0: ANSI/OEM
/ 1: UTF-16LE
/ 2: UTF-16BE
#define _STRF_ENCODE 3 / 3: UTF-8
/* 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(). / When _LFN_UNICODE is 0, this option has no effect. */
/
/ 0: ANSI/OEM
/ 1: UTF-16LE #define _FS_RPATH 0
/ 2: UTF-16BE /* This option configures relative path feature.
/ 3: UTF-8 /
/ / 0: Disable relative path feature and remove related functions.
/ When _LFN_UNICODE is 0, this option has no effect. */ / 1: Enable relative path feature. f_chdir() and f_chdrive() are available.
/ 2: f_getcwd() function is available in addition to 1.
/
#define _FS_RPATH 0 / Note that directory items read via f_readdir() are affected by this option. */
/* 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. / Drive/Volume Configurations
/ 2: f_getcwd() function is available in addition to 1. /---------------------------------------------------------------------------*/
/
/ Note that directory items read via f_readdir() are affected by this option. */ #define _VOLUMES 1
/* Number of volumes (logical drives) to be used. */
/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations #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.
#define _VOLUMES 1 / When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
/* Number of volumes (logical drives) to be used. */ / 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 _STR_VOLUME_ID 0
#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
/* _STR_VOLUME_ID option switches string volume ID feature. #define _MULTI_PARTITION 0
/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive /* This option switches multi-partition feature. By default (0), each logical drive
/ number in the path name. _VOLUME_STRS defines the drive ID strings for each / number is bound to the same physical drive number and only an FAT volume found on
/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for / the physical drive will be mounted. When multi-partition feature is enabled (1),
/ the drive ID strings are: A-Z and 0-9. */ / each logical drive number is bound to arbitrary physical drive and partition
/ listed in the VolToPart[]. Also f_fdisk() funciton will be available. */
#define _MULTI_PARTITION 0
/* This option switches multi-partition feature. By default (0), each logical drive #define _MIN_SS 512
/ number is bound to the same physical drive number and only an FAT volume found on #define _MAX_SS 512
/ the physical drive will be mounted. When multi-partition feature is enabled (1), /* These options configure the range of sector size to be supported. (512, 1024,
/ each logical drive number is bound to arbitrary physical drive and partition / 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
/ listed in the VolToPart[]. Also f_fdisk() funciton will be enabled. */ / 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
#define _MIN_SS 512 / disk_ioctl() function. */
#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 #define _USE_TRIM 0
/ harddisk. But a larger value may be required for on-board flash memory and some /* This option switches ATA-TRIM feature. (0:Disable or 1:Enable)
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured / To enable Trim feature, also CTRL_TRIM command should be implemented to the
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the / disk_ioctl() function. */
/ disk_ioctl() function. */
#define _FS_NOFSINFO 0
#define _USE_TRIM 0 /* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable) / option, and f_getfree() function at first time after volume mount will force
/ To enable Trim feature, also CTRL_TRIM command should be implemented to the / a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/ disk_ioctl() function. */ /
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
#define _FS_NOFSINFO 0 / bit1=0: Use last allocated cluster number in the FSINFO if available.
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this / bit1=1: Do not trust last allocated cluster number in the FSINFO.
/ 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. / System Configurations
/ bit1=1: Do not trust last allocated cluster number in the FSINFO. /---------------------------------------------------------------------------*/
*/
#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,
/ System Configurations / common sector buffer in the file system object (FATFS) is used for the file
/---------------------------------------------------------------------------*/ / data transfer. */
#define _FS_NORTC 0
#define _NORTC_MON 11 #define _FS_NORTC 0
#define _NORTC_MDAY 9 #define _NORTC_MON 1
#define _NORTC_YEAR 2014 #define _NORTC_MDAY 1
/* The _FS_NORTC option switches timestamp feature. If the system does not have #define _NORTC_YEAR 2015
/ an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable /* The _FS_NORTC option switches timestamp feature. If the system does not have
/ the timestamp feature. All objects modified by FatFs will have a fixed timestamp / an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable
/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR. / the timestamp feature. All objects modified by FatFs will have a fixed timestamp
/ When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need / defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR.
/ to be added to the project to read current time form RTC. _NORTC_MON, / When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need
/ _NORTC_MDAY and _NORTC_YEAR have no effect. / to be added to the project to read current time form RTC. _NORTC_MON,
/ These options have no effect at read-only configuration (_FS_READONLY == 1). */ / _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 #define _FS_LOCK 0
/ and illegal operation to open objects. This option must be 0 when _FS_READONLY /* The _FS_LOCK option switches file lock feature to control duplicated file open
/ is 1. / 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: Disable file lock feature. To avoid volume corruption, application program
/ >0: Enable file lock feature. The value defines how many files/sub-directories / should avoid illegal open, remove and rename to the open objects.
/ can be opened simultaneously under file lock control. Note that the file / >0: Enable file lock feature. The value defines how many files/sub-directories
/ lock feature is independent of re-entrancy. */ / 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 _FS_REENTRANT 1
#define _SYNC_t Semaphore * #define _FS_TIMEOUT 1000
/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs #define _SYNC_t Semaphore *
/ module itself. Note that regardless of this option, file access to different /* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs() / module itself. Note that regardless of this option, file access to different
/ and f_fdisk() function, are always not re-entrant. Only file/directory access / volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ to the same volume is under control of this feature. / 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, / 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() / 1: Enable re-entrancy. Also user provided synchronization handlers,
/ function, must be added to the project. Samples are available in / ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/ option/syscall.c. / 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*, / The _FS_TIMEOUT defines timeout period in unit of time tick.
/ SemaphoreHandle_t and etc.. */ / 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. #define _WORD_ACCESS 0
/ /* The _WORD_ACCESS option is an only platform dependent option. It defines
/ 0: Byte-by-byte access. Always compatible with all platforms. / which access method is used to the word data on the FAT volume.
/ 1: Word access. Do not choose this unless under both the following conditions. /
/ / 0: Byte-by-byte access. Always compatible with all platforms.
/ * Address misaligned memory access is always allowed to ALL instructions. / 1: Word access. Do not choose this unless under both the following conditions.
/ * Byte order on the memory is little-endian. /
/ / * Address misaligned memory access is always allowed to ALL instructions.
/ If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size. / * Byte order on the memory is little-endian.
/ Following table shows allowable settings of some processor types. /
/ / If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size.
/ ARM7TDMI 0 ColdFire 0 V850E 0 / Following table shows allowable settings of some type of processors.
/ Cortex-M3 0 Z80 0/1 V850ES 0/1 /
/ Cortex-M0 0 x86 0/1 TLCS-870 0/1 / ARM7TDMI 0 *2 ColdFire 0 *1 V850E 0 *2
/ AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1 / Cortex-M3 0 *3 Z80 0/1 V850ES 0/1
/ AVR32 0 RL78 0 R32C 0 / Cortex-M0 0 *2 x86 0/1 TLCS-870 0/1
/ PIC18 0/1 SH-2 0 M16C 0/1 / AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1
/ PIC24 0 H8S 0 MSP430 0 / AVR32 0 *1 RL78 0 *2 R32C 0 *2
/ PIC32 0 H8/300H 0 8051 0/1 / 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.
*/

View File

@ -21,13 +21,24 @@
#include "file.hpp" #include "file.hpp"
#include <algorithm>
File::~File() { File::~File() {
close(); 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); 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)); const auto seek_result = f_lseek(&f, f_size(&f));
if( seek_result == FR_OK ) { if( seek_result == FR_OK ) {
return true; 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); 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); const auto result = f_puts(string.c_str(), &f);
return (result >= 0); return (result >= 0);
} }
@ -69,3 +80,91 @@ bool File::sync() {
const auto result = f_sync(&f); const auto result = f_sync(&f);
return (result == FR_OK); 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 */

View File

@ -26,12 +26,17 @@
#include <cstddef> #include <cstddef>
#include <string> #include <string>
#include <array>
#include <memory>
#include <iterator>
class File { class File {
public: public:
~File(); ~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 close();
bool is_ready(); bool is_ready();
@ -39,14 +44,76 @@ public:
bool read(void* const data, const size_t bytes_to_read); bool read(void* const data, const size_t bytes_to_read);
bool write(const void* const data, const size_t bytes_to_write); 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(); bool sync();
private: private:
const std::string file_path;
FIL f; 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__*/ #endif/*__FILE_H__*/

View File

@ -261,7 +261,7 @@
* lower priority, this may slow down the driver a bit however. * lower priority, this may slow down the driver a bit however.
*/ */
#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__) #if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
#define SDC_NICE_WAITING TRUE #define SDC_NICE_WAITING FALSE
#endif #endif
/*===========================================================================*/ /*===========================================================================*/

View File

@ -27,7 +27,7 @@
using namespace lpc43xx; using namespace lpc43xx;
LogFile::LogFile( LogFile::LogFile(
const std::string file_path const std::string& file_path
) : file_path { file_path } ) : file_path { file_path }
{ {
file.open_for_append(file_path); file.open_for_append(file_path);

View File

@ -32,7 +32,7 @@ using namespace lpc43xx;
class LogFile { class LogFile {
public: public:
LogFile(const std::string file_path); LogFile(const std::string& file_path);
~LogFile(); ~LogFile();
bool is_ready(); bool is_ready();

View File

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

View File

@ -68,7 +68,7 @@
#include "event_m0.hpp" #include "event_m0.hpp"
#include "m4_startup.hpp" #include "core_control.hpp"
#include "spi_image.hpp" #include "spi_image.hpp"
#include "debug.hpp" #include "debug.hpp"
@ -76,9 +76,6 @@
#include "gcc.hpp" #include "gcc.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
#include "sd_card.hpp" #include "sd_card.hpp"
#include <string.h> #include <string.h>
@ -95,8 +92,6 @@ int main(void) {
sdcStart(&SDCD1, nullptr); sdcStart(&SDCD1, nullptr);
init_message_queues();
ui::Context context; ui::Context context;
ui::SystemView system_view { ui::SystemView system_view {
context, context,
@ -129,8 +124,7 @@ int main(void) {
portapack::shutdown(); portapack::shutdown();
m4_init(portapack::spi_flash::hackrf, portapack::memory::map::m4_code_hackrf); m4_init(portapack::spi_flash::hackrf, portapack::memory::map::m4_code_hackrf);
m0_halt();
rgu::reset(rgu::Reset::M0APP);
return 0; return 0;
} }

View File

@ -29,9 +29,8 @@ using namespace hackrf::one;
#include "clock_manager.hpp" #include "clock_manager.hpp"
#include "i2c_pp.hpp"
#include "touch_adc.hpp" #include "touch_adc.hpp"
#include "audio.hpp"
namespace portapack { namespace portapack {
@ -51,8 +50,6 @@ I2C i2c0(&I2CD0);
SPI ssp0(&SPID1); SPI ssp0(&SPID1);
SPI ssp1(&SPID2); SPI ssp1(&SPID2);
wolfson::wm8731::WM8731 audio_codec { i2c0, portapack::wm8731_i2c_address };
si5351::Si5351 clock_generator { si5351::Si5351 clock_generator {
i2c0, hackrf::one::si5351_i2c_address i2c0, hackrf::one::si5351_i2c_address
}; };
@ -134,9 +131,8 @@ void init() {
clock_manager.set_reference_ppb(persistent_memory::correction_ppb()); clock_manager.set_reference_ppb(persistent_memory::correction_ppb());
clock_manager.run_at_full_speed(); clock_manager.run_at_full_speed();
clock_manager.start_audio_pll(); audio::init();
audio_codec.init();
clock_manager.enable_first_if_clock(); clock_manager.enable_first_if_clock();
clock_manager.enable_second_if_clock(); clock_manager.enable_second_if_clock();
clock_manager.enable_codec_clocks(); clock_manager.enable_codec_clocks();
@ -149,7 +145,7 @@ void shutdown() {
display.shutdown(); display.shutdown();
radio::disable(); radio::disable();
audio_codec.reset(); audio::shutdown();
clock_manager.shutdown(); clock_manager.shutdown();
power.shutdown(); power.shutdown();

View File

@ -24,8 +24,8 @@
#include "receiver_model.hpp" #include "receiver_model.hpp"
#include "transmitter_model.hpp" #include "transmitter_model.hpp"
#include "i2c_pp.hpp"
#include "spi_pp.hpp" #include "spi_pp.hpp"
#include "wm8731.hpp"
#include "si5351.hpp" #include "si5351.hpp"
#include "lcd_ili9341.hpp" #include "lcd_ili9341.hpp"
@ -39,11 +39,10 @@ extern portapack::IO io;
extern lcd::ILI9341 display; extern lcd::ILI9341 display;
extern I2C i2c0;
extern SPI ssp0; extern SPI ssp0;
extern SPI ssp1; extern SPI ssp1;
extern wolfson::wm8731::WM8731 audio_codec;
extern si5351::Si5351 clock_generator; extern si5351::Si5351 clock_generator;
extern ClockManager clock_manager; extern ClockManager clock_manager;

View File

@ -22,9 +22,11 @@
#include "radio.hpp" #include "radio.hpp"
#include "rf_path.hpp" #include "rf_path.hpp"
#include "rffc507x.hpp"
#include "max2837.hpp"
#include "max5864.hpp" #include "max5864.hpp"
#include "baseband_cpld.hpp" #include "baseband_cpld.hpp"
#include "portapack_shared_memory.hpp"
#include "tuning.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); 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) { void set_baseband_decimation_by(const size_t n) {
baseband_cpld.set_decimation_by(n); baseband_cpld.set_decimation_by(n);
} }
@ -165,4 +171,43 @@ void disable() {
set_rf_amp(false); 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 */ } /* namespace radio */

View File

@ -27,11 +27,19 @@
#include <cstdint> #include <cstdint>
#include <cstddef> #include <cstddef>
#include "rffc507x.hpp"
#include "max2837.hpp"
namespace radio { 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 init();
void set_direction(const rf::Direction new_direction); 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_rf_amp(const bool rf_amp);
void set_lna_gain(const int_fast8_t db); void set_lna_gain(const int_fast8_t db);
void set_vga_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_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_baseband_decimation_by(const size_t n);
void set_antenna_bias(const bool on); void set_antenna_bias(const bool on);
void enable(Configuration configuration);
void configure(Configuration configuration);
void disable(); void disable();
extern rffc507x::RFFC507x first_if; namespace debug {
extern max2837::MAX2837 second_if;
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 */ } /* namespace radio */

View File

@ -21,90 +21,33 @@
#include "receiver_model.hpp" #include "receiver_model.hpp"
#include "portapack_shared_memory.hpp" #include "baseband_api.hpp"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
#include "portapack.hpp"
using namespace portapack; using namespace portapack;
#include "radio.hpp"
#include "audio.hpp"
#include "dsp_fir_taps.hpp" #include "dsp_fir_taps.hpp"
#include "dsp_iir.hpp" #include "dsp_iir.hpp"
#include "dsp_iir_config.hpp" #include "dsp_iir_config.hpp"
namespace { namespace {
struct AMConfig { static constexpr std::array<baseband::AMConfig, 3> am_configs { {
const fir_taps_complex<64> channel;
const AMConfigureMessage::Modulation modulation;
void apply() const;
};
struct NBFMConfig {
const fir_taps_real<24> decim_0;
const fir_taps_real<32> decim_1;
const fir_taps_real<32> channel;
const size_t deviation;
void apply() const;
};
struct WFMConfig {
void apply() const;
};
void AMConfig::apply() const {
const AMConfigureMessage message {
taps_6k0_decim_0,
taps_6k0_decim_1,
taps_6k0_decim_2,
channel,
modulation,
audio_12k_hpf_300hz_config
};
shared_memory.baseband_queue.push(message);
clock_manager.set_base_audio_clock_divider(4);
}
void NBFMConfig::apply() const {
const NBFMConfigureMessage message {
decim_0,
decim_1,
channel,
2,
deviation,
audio_24k_hpf_300hz_config,
audio_24k_deemph_300_6_config
};
shared_memory.baseband_queue.push(message);
clock_manager.set_base_audio_clock_divider(2);
}
void WFMConfig::apply() const {
const WFMConfigureMessage message {
taps_200k_wfm_decim_0,
taps_200k_wfm_decim_1,
taps_64_lp_156_198,
75000,
audio_48k_hpf_30hz_config,
audio_48k_deemph_2122_6_config
};
shared_memory.baseband_queue.push(message);
clock_manager.set_base_audio_clock_divider(1);
}
static constexpr std::array<AMConfig, 3> am_configs { {
{ taps_6k0_dsb_channel, AMConfigureMessage::Modulation::DSB }, { taps_6k0_dsb_channel, AMConfigureMessage::Modulation::DSB },
{ taps_2k8_usb_channel, AMConfigureMessage::Modulation::SSB }, { taps_2k8_usb_channel, AMConfigureMessage::Modulation::SSB },
{ taps_2k8_lsb_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_4k25_decim_0, taps_4k25_decim_1, taps_4k25_channel, 2500 },
{ taps_11k0_decim_0, taps_11k0_decim_1, taps_11k0_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 }, { 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; 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 { bool ReceiverModel::antenna_bias() const {
return antenna_bias_; return antenna_bias_;
} }
@ -217,18 +151,10 @@ void ReceiverModel::enable() {
update_headphone_volume(); update_headphone_volume();
} }
void ReceiverModel::baseband_disable() {
shared_memory.baseband_queue.push_and_wait(
BasebandConfigurationMessage {
.configuration = { },
}
);
}
void ReceiverModel::disable() { void ReceiverModel::disable() {
enabled_ = false; enabled_ = false;
update_antenna_bias(); update_antenna_bias();
baseband_disable(); baseband::stop();
// TODO: Responsibility for enabling/disabling the radio is muddy. // TODO: Responsibility for enabling/disabling the radio is muddy.
// Some happens in ReceiverModel, some inside radio namespace. // Some happens in ReceiverModel, some inside radio namespace.
@ -236,8 +162,7 @@ void ReceiverModel::disable() {
} }
int32_t ReceiverModel::tuning_offset() { int32_t ReceiverModel::tuning_offset() {
if( (baseband_configuration.mode == 4) || if( (baseband_configuration.mode == 4) ) {
(baseband_configuration.mode == 6) ) {
return 0; return 0;
} else { } else {
return -(sampling_rate() / 4); return -(sampling_rate() / 4);
@ -300,21 +225,20 @@ void ReceiverModel::update_baseband_configuration() {
// protocols that need quick RX/TX turn-around. // protocols that need quick RX/TX turn-around.
// Disabling baseband while changing sampling rates seems like a good idea... // 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(); update_tuning_frequency();
radio::set_baseband_decimation_by(baseband_oversampling()); radio::set_baseband_decimation_by(baseband_oversampling());
BasebandConfigurationMessage message { baseband_configuration }; baseband::start(baseband_configuration);
shared_memory.baseband_queue.push(message);
} }
void ReceiverModel::update_headphone_volume() { void ReceiverModel::update_headphone_volume() {
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do // TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
// both? // both?
audio_codec.set_headphone_volume(headphone_volume_); audio::headphone::set_volume(headphone_volume_);
} }
void ReceiverModel::update_modulation_configuration() { void ReceiverModel::update_modulation_configuration() {

View File

@ -36,10 +36,8 @@ public:
AMAudio = 0, AMAudio = 0,
NarrowbandFMAudio = 1, NarrowbandFMAudio = 1,
WidebandFMAudio = 2, WidebandFMAudio = 2,
AIS = 3,
SpectrumAnalysis = 4, SpectrumAnalysis = 4,
TPMS = 5, Capture = 7,
ERT = 6,
}; };
rf::Frequency tuning_frequency() const; rf::Frequency tuning_frequency() const;
@ -48,9 +46,6 @@ public:
rf::Frequency frequency_step() const; rf::Frequency frequency_step() const;
void set_frequency_step(rf::Frequency f); 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; bool antenna_bias() const;
void set_antenna_bias(bool enabled); void set_antenna_bias(bool enabled);
@ -122,8 +117,6 @@ private:
void update_am_configuration(); void update_am_configuration();
void update_nbfm_configuration(); void update_nbfm_configuration();
void update_wfm_configuration(); void update_wfm_configuration();
void baseband_disable();
}; };
#endif/*__RECEIVER_MODEL_H__*/ #endif/*__RECEIVER_MODEL_H__*/

View File

@ -129,7 +129,7 @@ public:
Entries& recent Entries& recent
) : recent { recent } ) : recent { recent }
{ {
flags.focusable = true; set_focusable(true);
} }
void paint(Painter& painter) override { void paint(Painter& painter) override {

View File

@ -278,21 +278,4 @@ spi::reg_t RFFC507x::readback(const Readback readback) {
return read(Register::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 */ } /* namespace rffc507x */

View File

@ -39,16 +39,12 @@ FRESULT mount() {
return f_mount(&fs, "", 0); return f_mount(&fs, "", 0);
} }
FRESULT unmount() {
return f_mount(NULL, "", 0);
}
} /* namespace */ } /* namespace */
Signal<Status> status_signal; Signal<Status> status_signal;
void poll_inserted() { 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 ) { if( card_present_now != card_present ) {
card_present = card_present_now; card_present = card_present_now;

View File

@ -54,7 +54,7 @@ std::vector<TemperatureLogger::sample_t> TemperatureLogger::history() const {
TemperatureLogger::sample_t TemperatureLogger::read_sample() { TemperatureLogger::sample_t TemperatureLogger::read_sample() {
// MAX2837 does not return a valid temperature if in "shutdown" mode. // 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) { void TemperatureLogger::push_sample(const TemperatureLogger::sample_t sample) {

View File

@ -22,9 +22,6 @@
#include "touch.hpp" #include "touch.hpp"
namespace touch { namespace touch {
float Manager::cmx;
float Manager::cmy;
struct Metrics { struct Metrics {
const float x; const float x;
@ -32,7 +29,7 @@ struct Metrics {
const float r; 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 /* TODO: Yikes! M0 doesn't have floating point, so this code is
* expensive! On the other hand, it seems to be working well (and * expensive! On the other hand, it seems to be working well (and
* fast *enough*?), so maybe leave it alone at least for now. * 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 { void Manager::feed(const Frame& frame) {
return {
static_cast<ui::Coord>(cmx),
static_cast<ui::Coord>(cmy)
};
}
void Manager::feed(const Frame frame) {
// touch_debounce.feed(touch_raw); // touch_debounce.feed(touch_raw);
const auto touch_raw = frame.touch; const auto touch_raw = frame.touch;
//const auto touch_stable = touch_debounce.state(); //const auto touch_stable = touch_debounce.state();
@ -91,9 +81,6 @@ void Manager::feed(const Frame frame) {
// TODO: Add touch pressure hysteresis? // TODO: Add touch pressure hysteresis?
touch_pressure = (metrics.r < r_touch_threshold); touch_pressure = (metrics.r < r_touch_threshold);
if( touch_pressure ) { if( touch_pressure ) {
cmx = metrics.x*100;
cmy = metrics.y*100;
const float x = width_pixels * (metrics.x - calib_x_low) / calib_x_range; const float x = width_pixels * (metrics.x - calib_x_low) / calib_x_range;
filter_x.feed(x); filter_x.feed(x);
const float y = height_pixels * (calib_y_high - metrics.y) / calib_y_range; const float y = height_pixels * (calib_y_high - metrics.y) / calib_y_range;

View File

@ -165,7 +165,7 @@ class Manager {
public: public:
std::function<void(ui::TouchEvent)> on_event; std::function<void(ui::TouchEvent)> on_event;
void feed(const Frame frame); void feed(const Frame& frame);
private: private:
enum State { enum State {

View File

@ -23,13 +23,10 @@
#include "event_m0.hpp" #include "event_m0.hpp"
#include "portapack.hpp" #include "baseband_api.hpp"
using namespace portapack;
#include "string_format.hpp" #include "string_format.hpp"
#include "crc.hpp"
#include "utility.hpp" #include "utility.hpp"
namespace tpms { namespace tpms {
@ -54,96 +51,20 @@ std::string temperature(Temperature temperature) {
} /* namespace format */ } /* 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 */ } /* 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(); const auto hex_formatted = packet.symbols_formatted();
if( log_file.is_ready() ) { 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! // 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; 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); log_file.write_entry(packet.received_at(), entry);
@ -235,25 +156,32 @@ TPMSAppView::TPMSAppView(NavigationView&) {
[this](Message* const p) { [this](Message* const p) {
const auto message = static_cast<const TPMSPacketMessage*>(p); const auto message = static_cast<const TPMSPacketMessage*>(p);
const tpms::Packet packet { message->packet }; 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, .mode = 5,
.sampling_rate = 2457600, .sampling_rate = sampling_rate,
.decimation_factor = 1, .decimation_factor = 1,
}); });
receiver_model.set_baseband_bandwidth(1750000);
receiver_model.set_rf_amp(false); logger = std::make_unique<TPMSLogger>("tpms.txt");
receiver_model.set_lna(32);
receiver_model.set_vga(32);
receiver_model.set_tuning_frequency(315000000);
receiver_model.enable();
} }
TPMSAppView::~TPMSAppView() { TPMSAppView::~TPMSAppView() {
receiver_model.disable(); baseband::stop();
radio::disable();
EventDispatcher::message_map().unregister_handler(Message::ID::TPMSPacket); 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() }); recent_entries_view.set_parent_rect({ 0, 0, new_parent_rect.width(), new_parent_rect.height() });
} }
void TPMSAppView::on_packet(const tpms::Packet& packet) { void TPMSAppView::on_packet(const tpms::SignalType signal_type, const tpms::Packet& packet) {
logger.on_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() ) { if( reading_opt.is_valid() ) {
const auto reading = reading_opt.value(); const auto reading = reading_opt.value();
recent.on_packet({ reading.type(), reading.id() }, reading); recent.on_packet({ reading.type(), reading.id() }, reading);
@ -282,4 +212,12 @@ void TPMSAppView::on_show_list() {
recent_entries_view.focus(); 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 */ } /* namespace ui */

View File

@ -25,127 +25,11 @@
#include "ui_widget.hpp" #include "ui_widget.hpp"
#include "ui_navigation.hpp" #include "ui_navigation.hpp"
#include "field_reader.hpp"
#include "baseband_packet.hpp"
#include "manchester.hpp"
#include "log_file.hpp" #include "log_file.hpp"
#include "recent_entries.hpp" #include "recent_entries.hpp"
#include "optional.hpp" #include "tpms_packet.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 */
namespace std { namespace std {
@ -186,10 +70,12 @@ using TPMSRecentEntries = RecentEntries<tpms::Reading, TPMSRecentEntry>;
class TPMSLogger { class TPMSLogger {
public: 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: private:
LogFile log_file { "tpms.txt" }; LogFile log_file;
}; };
namespace ui { namespace ui {
@ -212,13 +98,20 @@ public:
std::string title() const override { return "TPMS"; }; std::string title() const override { return "TPMS"; };
private: 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; TPMSRecentEntries recent;
TPMSLogger logger; std::unique_ptr<TPMSLogger> logger;
TPMSRecentEntriesView recent_entries_view { recent }; 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(); void on_show_list();
uint32_t target_frequency() const;
uint32_t tuning_frequency() const;
}; };
} /* namespace ui */ } /* namespace ui */

View File

@ -35,7 +35,7 @@ void Console::clear() {
display.scroll_set_position(0); display.scroll_set_position(0);
} }
void Console::write(const std::string message) { void Console::write(const std::string& message) {
const Style& s = style(); const Style& s = style();
const Font& font = s.font; const Font& font = s.font;
const auto rect = screen_rect(); 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); write(message);
crlf(); crlf();
} }

View File

@ -33,8 +33,8 @@ namespace ui {
class Console : public Widget { class Console : public Widget {
public: public:
void clear(); void clear();
void write(const std::string message); void write(const std::string& message);
void writeln(const std::string message); void writeln(const std::string& message);
void paint(Painter& painter) override; void paint(Painter& painter) override;

View File

@ -26,6 +26,10 @@
#include "radio.hpp" #include "radio.hpp"
#include "string_format.hpp" #include "string_format.hpp"
#include "audio.hpp"
#include "ui_sd_card_debug.hpp"
namespace ui { namespace ui {
/* DebugMemoryView *******************************************************/ /* DebugMemoryView *******************************************************/
@ -33,8 +37,8 @@ namespace ui {
DebugMemoryView::DebugMemoryView(NavigationView& nav) { DebugMemoryView::DebugMemoryView(NavigationView& nav) {
add_children({ { add_children({ {
&text_title, &text_title,
&text_label_m0_free, &text_label_m0_core_free,
&text_label_m0_free_value, &text_label_m0_core_free_value,
&text_label_m0_heap_fragmented_free, &text_label_m0_heap_fragmented_free,
&text_label_m0_heap_fragmented_free_value, &text_label_m0_heap_fragmented_free_value,
&text_label_m0_heap_fragments, &text_label_m0_heap_fragments,
@ -42,8 +46,8 @@ DebugMemoryView::DebugMemoryView(NavigationView& nav) {
&button_done &button_done
} }); } });
const auto m0_free = chCoreStatus(); const auto m0_core_free = chCoreStatus();
text_label_m0_free_value.set(to_string_dec_uint(m0_free, 5)); text_label_m0_core_free_value.set(to_string_dec_uint(m0_core_free, 5));
size_t m0_fragmented_free_space = 0; size_t m0_fragmented_free_space = 0;
const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space); const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space);
@ -240,68 +244,39 @@ void RegistersView::focus() {
button_done.focus(); button_done.focus();
} }
char hexify(char in) { /* DebugPeripheralsMenuView **********************************************/
if (in > 9) in += 7;
return in + 0x30;
}
DebugLCRView::DebugLCRView(NavigationView& nav, char * lcrstring, uint8_t checksum) { DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) {
char cstr[15] = "Checksum: 0x "; add_items<4>({ {
{ "RFFC5072", [&nav](){ nav.push<RegistersView>(
add_children({ { "RFFC5072", RegistersWidgetConfig { 31, 2, 4, 4 },
&text_lcr1, [](const size_t register_number) { return radio::debug::first_if::register_read(register_number); }
&text_lcr2, ); } },
&text_lcr3, { "MAX2837", [&nav](){ nav.push<RegistersView>(
&text_lcr4, "MAX2837", RegistersWidgetConfig { 32, 2, 3, 4 },
&text_lcr5, [](const size_t register_number) { return radio::debug::second_if::register_read(register_number); }
&text_checksum, ); } },
&button_done { "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); }
); } },
} }); } });
on_left = [&nav](){ nav.pop(); };
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();
} }
/* DebugMenuView *********************************************************/ /* DebugMenuView *********************************************************/
DebugMenuView::DebugMenuView(NavigationView& nav) { DebugMenuView::DebugMenuView(NavigationView& nav) {
add_items<8>({ { add_items<5>({ {
{ "Memory", ui::Color::white(), [&nav](){ nav.push<DebugMemoryView>(); } }, { "Memory", [&nav](){ nav.push<DebugMemoryView>(); } },
{ "Radio State", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } }, { "Radio State", [&nav](){ nav.push<NotImplementedView>(); } },
{ "SD Card", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } }, { "SD Card", [&nav](){ nav.push<SDCardDebugView>(); } },
{ "RFFC5072", ui::Color::white(), [&nav](){ nav.push<RegistersView>( { "Peripherals", [&nav](){ nav.push<DebugPeripheralsMenuView>(); } },
"RFFC5072", RegistersWidgetConfig { 31, 2, 4, 4 }, { "Temperature", [&nav](){ nav.push<TemperatureView>(); } },
[](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>(); } },
} }); } });
on_left = [&nav](){ nav.pop(); }; on_left = [&nav](){ nav.pop(); };
} }

View File

@ -36,8 +36,6 @@
#include <utility> #include <utility>
namespace ui { namespace ui {
char hexify(char in);
class DebugMemoryView : public View { class DebugMemoryView : public View {
public: public:
@ -51,12 +49,12 @@ private:
"Memory", "Memory",
}; };
Text text_label_m0_free { Text text_label_m0_core_free {
{ 0, 128, 104, 16 }, { 0, 128, 144, 16 },
"M0 Free Bytes", "M0 Core Free Bytes",
}; };
Text text_label_m0_free_value { Text text_label_m0_core_free_value {
{ 200, 128, 40, 16 }, { 200, 128, 40, 16 },
}; };
@ -279,6 +277,11 @@ private:
}; };
}; };
class DebugPeripheralsMenuView : public MenuView {
public:
DebugPeripheralsMenuView(NavigationView& nav);
};
class DebugMenuView : public MenuView { class DebugMenuView : public MenuView {
public: public:
DebugMenuView(NavigationView& nav); DebugMenuView(NavigationView& nav);

View File

@ -32,17 +32,19 @@ void MenuItemView::select() {
} }
void MenuItemView::highlight() { void MenuItemView::highlight() {
set_highlight(true); set_highlighted(true);
set_dirty();
} }
void MenuItemView::unhighlight() { void MenuItemView::unhighlight() {
set_highlight(false); set_highlighted(false);
set_dirty();
} }
void MenuItemView::paint(Painter& painter) { void MenuItemView::paint(Painter& painter) {
const auto r = screen_rect(); 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(); 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::~MenuView() { MenuView::~MenuView() {

View File

@ -36,6 +36,10 @@ struct MenuItem {
std::string text; std::string text;
ui::Color color; ui::Color color;
std::function<void(void)> on_select; 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 { class MenuItemView : public Widget {
@ -54,8 +58,6 @@ public:
private: private:
const MenuItem item; const MenuItem item;
void set_highlight(const bool value);
}; };
class MenuView : public View { class MenuView : public View {
@ -63,7 +65,7 @@ public:
std::function<void(void)> on_left; std::function<void(void)> on_left;
MenuView() { MenuView() {
flags.focusable = true; set_focusable(true);
} }
~MenuView(); ~MenuView();
@ -71,7 +73,7 @@ public:
void add_item(const MenuItem item); void add_item(const MenuItem item);
template<size_t N> 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) { for(const auto& item : items) {
add_item(item); add_item(item);
} }

View File

@ -23,37 +23,23 @@
#include "portapack.hpp" #include "portapack.hpp"
#include "event_m0.hpp" #include "event_m0.hpp"
#include "receiver_model.hpp"
#include "transmitter_model.hpp"
#include "portapack_persistent_memory.hpp"
#include "splash.hpp" #include "splash.hpp"
#include "ui_about.hpp" #include "ui_about.hpp"
#include "ui_setup.hpp" #include "ui_setup.hpp"
#include "ui_debug.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 "analog_audio_app.hpp"
#include "ais_app.hpp" #include "ais_app.hpp"
#include "ert_app.hpp" #include "ert_app.hpp"
#include "tpms_app.hpp" #include "tpms_app.hpp"
#include "capture_app.hpp"
#include "m4_startup.hpp" #include "core_control.hpp"
#include "spi_image.hpp"
#include "modules.h" #include "file.hpp"
#include "png_writer.hpp"
using namespace portapack;
namespace ui { namespace ui {
@ -63,6 +49,7 @@ SystemStatusView::SystemStatusView() {
add_children({ { add_children({ {
&button_back, &button_back,
&title, &title,
&button_camera,
&button_sleep, &button_sleep,
&sd_card_status_view, &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&) { button_sleep.on_select = [this](ImageButton&) {
DisplaySleepMessage message; DisplaySleepMessage message;
EventDispatcher::message_map().send(&message); EventDispatcher::message_map().send(&message);
}; };
} }
void SystemStatusView::set_back_visible(bool new_value) { void SystemStatusView::set_back_enabled(bool new_value) {
button_back.hidden(!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) { 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 ************************************************************/ /* Navigation ************************************************************/
bool NavigationView::is_top() const { bool NavigationView::is_top() const {
@ -108,10 +115,6 @@ View* NavigationView::push_view(std::unique_ptr<View> new_view) {
return p; return p;
} }
void NavigationView::push(View* v) {
push_view(std::unique_ptr<View>(v));
}
void NavigationView::pop() { void NavigationView::pop() {
// Can't pop last item from stack. // Can't pop last item from stack.
if( view_stack.size() > 1 ) { if( view_stack.size() > 1 ) {
@ -153,25 +156,37 @@ void NavigationView::focus() {
TranspondersMenuView::TranspondersMenuView(NavigationView& nav) { TranspondersMenuView::TranspondersMenuView(NavigationView& nav) {
add_items<3>({ { add_items<3>({ {
{ "AIS: Boats", ui::Color::white(), [&nav](){ nav.push<AISAppView>(); } }, { "AIS: Boats", [&nav](){ nav.push<AISAppView>(); } },
{ "ERT: Utility Meters", ui::Color::white(), [&nav](){ nav.push<ERTAppView>(); } }, { "ERT: Utility Meters", [&nav](){ nav.push<ERTAppView>(); } },
{ "TPMS: Cars", ui::Color::white(), [&nav](){ nav.push<TPMSAppView>(); } }, { "TPMS: Cars", [&nav](){ nav.push<TPMSAppView>(); } },
} }); } });
on_left = [&nav](){ nav.pop(); };
} }
/* ReceiverMenuView ******************************************************/ /* ReceiverMenuView ******************************************************/
ReceiverMenuView::ReceiverMenuView(NavigationView& nav) { ReceiverMenuView::ReceiverMenuView(NavigationView& nav) {
add_items<2>({ { add_items<2>({ {
{ "Audio", ui::Color::white(), [&nav](){ nav.push<AnalogAudioView>(); } }, { "Audio", [&nav](){ nav.push<AnalogAudioView>(); } },
{ "Transponders", ui::Color::white(), [&nav](){ nav.push<TranspondersMenuView>(); } }, { "Transponders", [&nav](){ nav.push<TranspondersMenuView>(); } },
} }); } });
on_left = [&nav](){ nav.pop(); };
} }
/* SystemMenuView ********************************************************/ /* SystemMenuView ********************************************************/
SystemMenuView::SystemMenuView(NavigationView& nav) { 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); } }, { "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(false); } },
{ "Receiver", ui::Color::cyan(), [&nav](){ nav.push<LoadModuleView>(md5_baseband, new ReceiverMenuView(nav)); } }, { "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 }); } }, //{ "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>(); } }, { "About", ui::Color::white(), [&nav](){ nav.push<AboutView>(); } },
{ "Debug", ui::Color::white(), [&nav](){ nav.push<DebugMenuView>(); } }, { "Debug", ui::Color::white(), [&nav](){ nav.push<DebugMenuView>(); } },
{ "HackRF", ui::Color::white(), [&nav](){ nav.push<HackRFFirmwareView>(); } }, { "HackRF", ui::Color::white(), [&nav](){ nav.push<HackRFFirmwareView>(); } },
} }); } });*/
} }
/* SystemView ************************************************************/ /* SystemView ************************************************************/
@ -207,7 +222,7 @@ SystemView::SystemView(
) : View { parent_rect }, ) : View { parent_rect },
context_(context) context_(context)
{ {
style_ = &style_default; set_style(&style_default);
constexpr ui::Dim status_view_height = 16; 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) } { parent_rect.width(), static_cast<ui::Dim>(parent_rect.height() - status_view_height) }
}); });
navigation_view.on_view_changed = [this](const View& new_view) { 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()); this->status_view.set_title(new_view.title());
}; };

View File

@ -60,21 +60,46 @@ static constexpr Bitmap bitmap_sleep {
{ 16, 16 }, bitmap_sleep_data { 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 { class SystemStatusView : public View {
public: public:
std::function<void(void)> on_back; std::function<void(void)> on_back;
SystemStatusView(); SystemStatusView();
void set_back_visible(bool new_value); void set_back_enabled(bool new_value);
void set_title(const std::string new_value); void set_title(const std::string new_value);
private: private:
static constexpr auto default_title = "PortaPack"; static constexpr auto default_title = "PortaPack";
static constexpr auto back_text_enabled = " < ";
static constexpr auto back_text_disabled = " * ";
Button button_back { Button button_back {
{ 0 * 8, 0 * 16, 3 * 8, 16 }, { 0 * 8, 0 * 16, 3 * 8, 16 },
" < ", back_text_disabled,
}; };
Text title { Text title {
@ -82,6 +107,13 @@ private:
default_title, default_title,
}; };
ImageButton button_camera {
{ 22 * 8, 0, 2 * 8, 1 * 16 },
&bitmap_camera,
Color::white(),
Color::black()
};
ImageButton button_sleep { ImageButton button_sleep {
{ 25 * 8, 0, 2 * 8, 1 * 16 }, { 25 * 8, 0, 2 * 8, 1 * 16 },
&bitmap_sleep, &bitmap_sleep,
@ -92,6 +124,8 @@ private:
SDCardStatusView sd_card_status_view { SDCardStatusView sd_card_status_view {
{ 28 * 8, 0 * 16, 2 * 8, 1 * 16 } { 28 * 8, 0 * 16, 2 * 8, 1 * 16 }
}; };
void on_camera();
}; };
class NavigationView : public View { class NavigationView : public View {

View File

@ -38,7 +38,7 @@ FrequencyField::FrequencyField(
length_ { 11 }, length_ { 11 },
range(rf::tuning_range) range(rf::tuning_range)
{ {
flags.focusable = true; set_focusable(true);
} }
rf::Frequency FrequencyField::value() const { 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 */ } /* namespace ui */

View File

@ -315,6 +315,15 @@ public:
void on_focus() override; 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 */ } /* namespace ui */
#endif/*__UI_RECEIVER_H__*/ #endif/*__UI_RECEIVER_H__*/

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

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

View File

@ -54,8 +54,8 @@ static constexpr Bitmap bitmap_sd_card_unknown {
static constexpr uint8_t bitmap_sd_card_error_data[] = { static constexpr uint8_t bitmap_sd_card_error_data[] = {
0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f,
0xf8, 0x1f, 0xf8, 0x1b, 0xf8, 0x19, 0xf8, 0x1c, 0xf8, 0x1f, 0xd8, 0x1b, 0x98, 0x19, 0x38, 0x1c,
0xf8, 0x1e, 0xf8, 0x1c, 0xf8, 0x19, 0xf8, 0x1b, 0x78, 0x1e, 0x38, 0x1c, 0x98, 0x19, 0xd8, 0x1b,
0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
}; };

View File

@ -85,7 +85,7 @@ void SetDateTimeView::focus() {
button_cancel.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_year.set_value(model.year);
field_month.set_value(model.month); field_month.set_value(model.month);
field_day.set_value(model.day); field_day.set_value(model.day);
@ -137,7 +137,7 @@ void SetFrequencyCorrectionView::focus() {
button_cancel.focus(); button_cancel.focus();
} }
void SetFrequencyCorrectionView::form_init(const SetFrequencyCorrectionModel model) { void SetFrequencyCorrectionView::form_init(const SetFrequencyCorrectionModel& model) {
field_ppm.set_value(model.ppm); field_ppm.set_value(model.ppm);
} }

View File

@ -42,9 +42,6 @@ struct SetDateTimeModel {
class SetDateTimeView : public View { class SetDateTimeView : public View {
public: public:
std::function<void(SetDateTimeModel)> on_ok;
std::function<void()> on_cancel;
SetDateTimeView(NavigationView& nav); SetDateTimeView(NavigationView& nav);
void focus() override; void focus() override;
@ -129,7 +126,7 @@ private:
"Cancel", "Cancel",
}; };
void form_init(const SetDateTimeModel model); void form_init(const SetDateTimeModel& model);
SetDateTimeModel form_collect(); SetDateTimeModel form_collect();
}; };
@ -139,9 +136,6 @@ struct SetFrequencyCorrectionModel {
class SetFrequencyCorrectionView : public View { class SetFrequencyCorrectionView : public View {
public: public:
std::function<void(SetFrequencyCorrectionModel)> on_ok;
std::function<void()> on_cancel;
SetFrequencyCorrectionView(NavigationView& nav); SetFrequencyCorrectionView(NavigationView& nav);
void focus() override; void focus() override;
@ -173,7 +167,7 @@ private:
"Cancel", "Cancel",
}; };
void form_init(const SetFrequencyCorrectionModel model); void form_init(const SetFrequencyCorrectionModel& model);
SetFrequencyCorrectionModel form_collect(); SetFrequencyCorrectionModel form_collect();
}; };

View File

@ -26,9 +26,10 @@
#include "spectrum_color_lut.hpp" #include "spectrum_color_lut.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "portapack_shared_memory.hpp"
using namespace portapack; using namespace portapack;
#include "baseband_api.hpp"
#include "string_format.hpp" #include "string_format.hpp"
#include <cmath> #include <cmath>
@ -251,19 +252,11 @@ void WaterfallWidget::on_show() {
} }
); );
shared_memory.baseband_queue.push_and_wait( baseband::spectrum_streaming_start();
SpectrumStreamingConfigMessage {
SpectrumStreamingConfigMessage::Mode::Running
}
);
} }
void WaterfallWidget::on_hide() { void WaterfallWidget::on_hide() {
shared_memory.baseband_queue.push_and_wait( baseband::spectrum_streaming_stop();
SpectrumStreamingConfigMessage {
SpectrumStreamingConfigMessage::Mode::Stopped
}
);
EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync); EventDispatcher::message_map().unregister_handler(Message::ID::DisplayFrameSync);
EventDispatcher::message_map().unregister_handler(Message::ID::ChannelSpectrumConfig); EventDispatcher::message_map().unregister_handler(Message::ID::ChannelSpectrumConfig);

View File

@ -127,6 +127,7 @@ CPPSRC = main.cpp \
message_queue.cpp \ message_queue.cpp \
event.cpp \ event.cpp \
event_m4.cpp \ event_m4.cpp \
thread_wait.cpp \
gpdma.cpp \ gpdma.cpp \
baseband_dma.cpp \ baseband_dma.cpp \
baseband_sgpio.cpp \ baseband_sgpio.cpp \
@ -145,6 +146,7 @@ CPPSRC = main.cpp \
proc_wideband_spectrum.cpp \ proc_wideband_spectrum.cpp \
proc_tpms.cpp \ proc_tpms.cpp \
proc_ert.cpp \ proc_ert.cpp \
proc_capture.cpp \
dsp_squelch.cpp \ dsp_squelch.cpp \
clock_recovery.cpp \ clock_recovery.cpp \
packet_builder.cpp \ packet_builder.cpp \
@ -155,7 +157,7 @@ CPPSRC = main.cpp \
rssi.cpp \ rssi.cpp \
rssi_dma.cpp \ rssi_dma.cpp \
rssi_thread.cpp \ rssi_thread.cpp \
audio.cpp \ audio_compressor.cpp \
audio_output.cpp \ audio_output.cpp \
audio_dma.cpp \ audio_dma.cpp \
audio_stats_collector.cpp \ audio_stats_collector.cpp \

View File

@ -0,0 +1,52 @@
/*
* 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_compressor.hpp"
float GainComputer::operator()(const float x) const {
const auto abs_x = std::abs(x);
const auto db = (abs_x < lin_floor) ? db_floor : log2_db_k * fast_log2(abs_x);
const auto overshoot_db = db - threshold_db;
if( knee_width_db > 0.0f ) {
const auto w2 = knee_width_db / 2.0f;
const auto a = w2 / (knee_width_db * knee_width_db);
const auto in_transition = (overshoot_db > -w2) && (overshoot_db < w2);
const auto rectified_overshoot = in_transition ? (a * std::pow(overshoot_db + w2, 2.0f)) : std::max(overshoot_db, 0.0f);
return rectified_overshoot * slope;
} else {
const auto rectified_overshoot = std::max(overshoot_db, 0.0f);
return rectified_overshoot * slope;
}
}
void FeedForwardCompressor::execute_in_place(const buffer_f32_t& buffer) {
constexpr float makeup_gain = std::pow(10.0f, (threshold - (threshold / ratio)) / -20.0f);
for(size_t i=0; i<buffer.count; i++) {
buffer.p[i] = execute_once(buffer.p[i]) * makeup_gain;
}
}
float FeedForwardCompressor::execute_once(const float x) {
const auto gain_db = gain_computer(x);
const auto peak_db = -peak_detector(-gain_db);
const auto gain = fast_pow2(peak_db * (3.321928094887362f / 20.0f));
return x * gain;
}

View File

@ -0,0 +1,102 @@
/*
* 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_COMPRESSOR_H__
#define __AUDIO_COMPRESSOR_H__
#include "dsp_types.hpp"
#include "utility.hpp"
#include <cmath>
/* Code based on article in Journal of the Audio Engineering Society
* Vol. 60, No. 6, 2012 June, by Dimitrios Giannoulis, Michael Massberg,
* Joshua D. Reiss "Digital Dynamic Range Compressor Design A Tutorial
* and Analysis"
*/
class GainComputer {
public:
constexpr GainComputer(
float ratio,
float threshold
) : ratio { ratio },
slope { 1.0f / ratio - 1.0f },
threshold_db { threshold }
{
}
float operator()(const float x) const;
private:
const float ratio;
const float slope;
const float threshold_db;
static constexpr float knee_width_db = 0.0f;
static constexpr float db_floor = -120.0f;
static constexpr float lin_floor = std::pow(10.0f, db_floor / 20.0f);
static constexpr float log2_db_k = 20.0f * std::log10(2.0f);
};
class PeakDetectorBranchingSmooth {
public:
constexpr PeakDetectorBranchingSmooth(
float att_a,
float rel_a
) : att_a { att_a },
rel_a { rel_a }
{
}
float operator()(const float db) {
const auto a = (db > state) ? att_a : rel_a;
state = db + a * (state - db);
return state;
}
private:
float state { 0.0f };
const float att_a;
const float rel_a;
};
class FeedForwardCompressor {
public:
void execute_in_place(const buffer_f32_t& buffer);
private:
static constexpr float fs = 12000.0f;
static constexpr float ratio = 10.0f;
static constexpr float threshold = -30.0f;
GainComputer gain_computer { ratio, threshold };
PeakDetectorBranchingSmooth peak_detector { tau_alpha(0.010f, fs), tau_alpha(0.300f, fs) };
float execute_once(const float x);
static constexpr float tau_alpha(const float tau, const float fs) {
return std::exp(-1.0f / (tau * fs));
}
};
#endif/*__AUDIO_COMPRESSOR_H__*/

View File

@ -24,9 +24,22 @@
#include <cstdint> #include <cstdint>
#include "audio.hpp" #include "buffer.hpp"
namespace audio { namespace audio {
struct sample_t {
union {
struct {
int16_t left;
int16_t right;
};
uint32_t raw;
};
};
using buffer_t = buffer_t<sample_t>;
namespace dma { namespace dma {
void init(); void init();

View File

@ -46,7 +46,7 @@ void AudioOutput::write(
) { ) {
std::array<float, 32> audio_f; std::array<float, 32> audio_f;
for(size_t i=0; i<audio.count; i++) { for(size_t i=0; i<audio.count; i++) {
audio_f[i] = audio.p[i]; audio_f[i] = audio.p[i] * ki;
} }
write(buffer_f32_t { write(buffer_f32_t {
audio_f.data(), audio_f.data(),
@ -77,24 +77,27 @@ void AudioOutput::on_block(
audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0); audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0);
const bool audio_present = (audio_present_history != 0); const bool audio_present = (audio_present_history != 0);
if( audio_present ) { if( !audio_present ) {
i2s::i2s0::tx_unmute();
} else {
i2s::i2s0::tx_mute();
for(size_t i=0; i<audio.count; i++) { for(size_t i=0; i<audio.count; i++) {
audio.p[i] = 0; audio.p[i] = 0;
} }
} }
fill_audio_buffer(audio); fill_audio_buffer(audio, audio_present);
} }
void AudioOutput::fill_audio_buffer(const buffer_f32_t& audio) { void AudioOutput::fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo) {
std::array<int16_t, 32> audio_int;
auto audio_buffer = audio::dma::tx_empty_buffer(); auto audio_buffer = audio::dma::tx_empty_buffer();
for(size_t i=0; i<audio_buffer.count; i++) { for(size_t i=0; i<audio_buffer.count; i++) {
const int32_t sample_int = audio.p[i]; const int32_t sample_int = audio.p[i] * k;
const int32_t sample_saturated = __SSAT(sample_int, 16); const int32_t sample_saturated = __SSAT(sample_int, 16);
audio_buffer.p[i].left = audio_buffer.p[i].right = sample_saturated; audio_buffer.p[i].left = audio_buffer.p[i].right = sample_saturated;
audio_int[i] = sample_saturated;
}
if( send_to_fifo ) {
stream.write(audio_int.data(), audio_buffer.count * sizeof(audio_int[0]));
} }
feed_audio_stats(audio); feed_audio_stats(audio);

View File

@ -27,6 +27,7 @@
#include "dsp_iir.hpp" #include "dsp_iir.hpp"
#include "dsp_squelch.hpp" #include "dsp_squelch.hpp"
#include "stream_input.hpp"
#include "block_decimator.hpp" #include "block_decimator.hpp"
#include "audio_stats_collector.hpp" #include "audio_stats_collector.hpp"
@ -44,18 +45,23 @@ public:
void write(const buffer_f32_t& audio); void write(const buffer_f32_t& audio);
private: private:
static constexpr float k = 32768.0f;
static constexpr float ki = 1.0f / k;
BlockDecimator<float, 32> block_buffer { 1 }; BlockDecimator<float, 32> block_buffer { 1 };
IIRBiquadFilter hpf; IIRBiquadFilter hpf;
IIRBiquadFilter deemph; IIRBiquadFilter deemph;
FMSquelch squelch; FMSquelch squelch;
StreamInput stream { 14 };
AudioStatsCollector audio_stats; AudioStatsCollector audio_stats;
uint64_t audio_present_history = 0; uint64_t audio_present_history = 0;
void on_block(const buffer_f32_t& audio); void on_block(const buffer_f32_t& audio);
void fill_audio_buffer(const buffer_f32_t& audio); void fill_audio_buffer(const buffer_f32_t& audio, const bool send_to_fifo);
void feed_audio_stats(const buffer_f32_t& audio); void feed_audio_stats(const buffer_f32_t& audio);
}; };

View File

@ -42,8 +42,8 @@ bool AudioStatsCollector::update_stats(const size_t sample_count, const size_t s
const size_t samples_per_update = sampling_rate * update_interval; const size_t samples_per_update = sampling_rate * update_interval;
if( count >= samples_per_update ) { if( count >= samples_per_update ) {
statistics.rms_db = complex16_mag_squared_to_dbv_norm(squared_sum / count); statistics.rms_db = mag2_to_dbv_norm(squared_sum / count);
statistics.max_db = complex16_mag_squared_to_dbv_norm(max_squared); statistics.max_db = mag2_to_dbv_norm(max_squared);
statistics.count = count; statistics.count = count;
squared_sum = 0; squared_sum = 0;

View File

@ -32,6 +32,8 @@ using namespace lpc43xx;
#include "portapack_dma.hpp" #include "portapack_dma.hpp"
#include "thread_wait.hpp"
namespace baseband { namespace baseband {
namespace dma { namespace dma {
@ -99,21 +101,19 @@ constexpr size_t msg_count = transfers_per_buffer - 1;
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_loop; static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_loop;
static constexpr auto& gpdma_channel_sgpio = gpdma::channels[portapack::sgpio_gpdma_channel_number]; static constexpr auto& gpdma_channel_sgpio = gpdma::channels[portapack::sgpio_gpdma_channel_number];
static Semaphore semaphore; static ThreadWait thread_wait;
static volatile const gpdma::channel::LLI* next_lli = nullptr;
static void transfer_complete() { static void transfer_complete() {
next_lli = gpdma_channel_sgpio.next_lli(); const auto next_lli_index = gpdma_channel_sgpio.next_lli() - &lli_loop[0];
chSemSignalI(&semaphore); thread_wait.wake_from_interrupt(next_lli_index);
} }
static void dma_error() { static void dma_error() {
thread_wait.wake_from_interrupt(-1);
disable(); disable();
} }
void init() { void init() {
chSemInit(&semaphore, 0);
gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error); gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error);
// LPC_GPDMA->SYNC |= (1 << gpdma_src_peripheral); // LPC_GPDMA->SYNC |= (1 << gpdma_src_peripheral);
@ -138,9 +138,6 @@ void configure(
void enable(const baseband::Direction direction) { void enable(const baseband::Direction direction) {
const auto gpdma_config = config(direction); const auto gpdma_config = config(direction);
gpdma_channel_sgpio.configure(lli_loop[0], gpdma_config); gpdma_channel_sgpio.configure(lli_loop[0], gpdma_config);
chSemReset(&semaphore, 0);
gpdma_channel_sgpio.enable(); gpdma_channel_sgpio.enable();
} }
@ -153,16 +150,11 @@ void disable() {
} }
baseband::buffer_t wait_for_rx_buffer() { baseband::buffer_t wait_for_rx_buffer() {
const auto status = chSemWait(&semaphore); const auto next_index = thread_wait.sleep();
if( status == RDY_OK ) {
const auto next = next_lli; if( next_index >= 0 ) {
if( next ) { const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
const size_t next_index = next - &lli_loop[0]; return { reinterpret_cast<sample_t*>(lli_loop[free_index].destaddr), transfer_samples };
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return { reinterpret_cast<sample_t*>(lli_loop[free_index].destaddr), transfer_samples };
} else {
return { };
}
} else { } else {
return { }; return { };
} }

View File

@ -38,6 +38,7 @@
#include "proc_wideband_spectrum.hpp" #include "proc_wideband_spectrum.hpp"
#include "proc_tpms.hpp" #include "proc_tpms.hpp"
#include "proc_ert.hpp" #include "proc_ert.hpp"
#include "proc_capture.hpp"
#include "portapack_shared_memory.hpp" #include "portapack_shared_memory.hpp"
@ -85,7 +86,7 @@ void BasebandThread::run() {
baseband_sgpio.init(); baseband_sgpio.init();
baseband::dma::init(); baseband::dma::init();
const auto baseband_buffer = new std::array<baseband::sample_t, 8192>(); const auto baseband_buffer = std::make_unique<std::array<baseband::sample_t, 8192>>();
baseband::dma::configure( baseband::dma::configure(
baseband_buffer->data(), baseband_buffer->data(),
direction() direction()
@ -123,22 +124,6 @@ void BasebandThread::run() {
delete baseband_buffer; delete baseband_buffer;
} }
char ram_loop[32];
typedef int (*fn_ptr)(void);
fn_ptr loop_ptr;
void ram_loop_fn(void) {
while(1) {}
}
void BasebandThread::wait_for_switch(void) {
memcpy(&ram_loop[0], reinterpret_cast<char*>(&ram_loop_fn), 32);
loop_ptr = reinterpret_cast<fn_ptr>(&ram_loop[0]);
ReadyForSwitchMessage message;
shared_memory.application_queue.push(message);
(*loop_ptr)();
}
BasebandProcessor* BasebandThread::create_processor(const int32_t mode) { BasebandProcessor* BasebandThread::create_processor(const int32_t mode) {
switch(mode) { switch(mode) {
case 0: return new NarrowbandAMAudio(); case 0: return new NarrowbandAMAudio();
@ -148,7 +133,7 @@ BasebandProcessor* BasebandThread::create_processor(const int32_t mode) {
case 4: return new WidebandSpectrum(); case 4: return new WidebandSpectrum();
case 5: return new TPMSProcessor(); case 5: return new TPMSProcessor();
case 6: return new ERTProcessor(); case 6: return new ERTProcessor();
case 255: wait_for_switch(); case 7: return new CaptureProcessor();
default: return nullptr; default: return nullptr;
} }
} }

View File

@ -30,11 +30,6 @@
class BasebandThread : public ThreadBase { class BasebandThread : public ThreadBase {
public: public:
BasebandThread(
) : ThreadBase { "baseband" }
{
}
Thread* start(const tprio_t priority); Thread* start(const tprio_t priority);
void on_message(const Message* const message); void on_message(const Message* const message);
@ -49,9 +44,10 @@ public:
Thread* thread_main { nullptr }; Thread* thread_main { nullptr };
Thread* thread_rssi { nullptr }; Thread* thread_rssi { nullptr };
BasebandProcessor* baseband_processor { nullptr };
private: private:
BasebandProcessor* baseband_processor { nullptr };
BasebandConfiguration baseband_configuration; BasebandConfiguration baseband_configuration;
void run() override; void run() override;

View File

@ -49,7 +49,7 @@ public:
if( count >= samples_per_update ) { if( count >= samples_per_update ) {
const float max_squared_f = max_squared; const float max_squared_f = max_squared;
const int32_t max_db = complex16_mag_squared_to_dbv_norm(max_squared_f); const int32_t max_db = mag2_to_dbv_norm(max_squared_f * (1.0f / (32768.0f * 32768.0f)));
callback({ max_db, count }); callback({ max_db, count });
max_squared = 0; max_squared = 0;

View File

@ -129,7 +129,7 @@
* @note The default is @p TRUE. * @note The default is @p TRUE.
*/ */
#if !defined(CH_USE_REGISTRY) || defined(__DOXYGEN__) #if !defined(CH_USE_REGISTRY) || defined(__DOXYGEN__)
#define CH_USE_REGISTRY TRUE #define CH_USE_REGISTRY FALSE
#endif #endif
/** /**

View File

@ -116,19 +116,21 @@ private:
template<typename ErrorFilter> template<typename ErrorFilter>
class ClockRecovery { class ClockRecovery {
public: public:
using SymbolHandler = std::function<void(const float)>;
ClockRecovery( ClockRecovery(
const float sampling_rate, const float sampling_rate,
const float symbol_rate, const float symbol_rate,
ErrorFilter error_filter, ErrorFilter error_filter,
std::function<void(const float)> symbol_handler SymbolHandler symbol_handler
) : symbol_handler { symbol_handler } ) : symbol_handler { std::move(symbol_handler) }
{ {
configure(sampling_rate, symbol_rate, error_filter); configure(sampling_rate, symbol_rate, error_filter);
} }
ClockRecovery( ClockRecovery(
std::function<void(const float)> symbol_handler SymbolHandler symbol_handler
) : symbol_handler { symbol_handler } ) : symbol_handler { std::move(symbol_handler) }
{ {
} }
@ -155,7 +157,7 @@ private:
dsp::interpolation::LinearResampler resampler; dsp::interpolation::LinearResampler resampler;
GardnerTimingErrorDetector timing_error_detector; GardnerTimingErrorDetector timing_error_detector;
ErrorFilter error_filter; ErrorFilter error_filter;
std::function<void(const float)> symbol_handler; const SymbolHandler symbol_handler;
void resampler_callback(const float interpolated_sample) { void resampler_callback(const float interpolated_sample) {
timing_error_detector(interpolated_sample, timing_error_detector(interpolated_sample,
@ -166,7 +168,12 @@ private:
} }
void symbol_callback(const float symbol, const float lateness) { void symbol_callback(const float symbol, const float lateness) {
symbol_handler(symbol); // NOTE: This check is to avoid std::function nullptr check, which
// brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code.
// TODO: Make symbol_handler known at compile time.
if( symbol_handler) {
symbol_handler(symbol);
}
const float adjustment = error_filter(lateness); const float adjustment = error_filter(lateness);
resampler.advance(adjustment); resampler.advance(adjustment);

View File

@ -177,6 +177,20 @@ static inline uint32_t scale_round_and_pack(
return __PKHBT(saturated_real, saturated_imag, 16); return __PKHBT(saturated_real, saturated_imag, 16);
} }
template<typename Tap>
static void taps_copy(
const Tap* const source,
Tap* const target,
const size_t count,
const bool shift_up
) {
const uint32_t negate_pattern = shift_up ? 0b1110 : 0b0100;
for(size_t i=0; i<count; i++) {
const bool negate = (negate_pattern >> (i & 3)) & 1;
target[i] = negate ? -source[i] : source[i];
}
}
// FIRC8xR16x24FS4Decim4 ////////////////////////////////////////////////// // FIRC8xR16x24FS4Decim4 //////////////////////////////////////////////////
void FIRC8xR16x24FS4Decim4::configure( void FIRC8xR16x24FS4Decim4::configure(
@ -184,13 +198,7 @@ void FIRC8xR16x24FS4Decim4::configure(
const int32_t scale, const int32_t scale,
const Shift shift const Shift shift
) { ) {
const int negate_factor = (shift == Shift::Up) ? -1 : 1; taps_copy(taps.data(), taps_.data(), taps_.size(), shift == Shift::Up);
for(size_t i=0; i<taps.size(); i+=4) {
taps_[i+0] = taps[i+0];
taps_[i+1] = taps[i+1] * negate_factor;
taps_[i+2] = -taps[i+2];
taps_[i+3] = taps[i+3] * negate_factor;
}
output_scale = scale; output_scale = scale;
z_.fill({}); z_.fill({});
} }
@ -246,13 +254,7 @@ void FIRC8xR16x24FS4Decim8::configure(
const int32_t scale, const int32_t scale,
const Shift shift const Shift shift
) { ) {
const int negate_factor = (shift == Shift::Up) ? -1 : 1; taps_copy(taps.data(), taps_.data(), taps_.size(), shift == Shift::Up);
for(size_t i=0; i<taps.size(); i+=4) {
taps_[i+0] = taps[i+0];
taps_[i+1] = taps[i+1] * negate_factor;
taps_[i+2] = -taps[i+2];
taps_[i+3] = taps[i+3] * negate_factor;
}
output_scale = scale; output_scale = scale;
z_.fill({}); z_.fill({});
} }
@ -563,20 +565,18 @@ buffer_c16_t DecimateBy2CIC3::execute(
*/ */
uint32_t t1 = _iq0; uint32_t t1 = _iq0;
uint32_t t2 = _iq1; uint32_t t2 = _iq1;
uint32_t t3, t4;
const uint32_t taps = 0x00000003; const uint32_t taps = 0x00000003;
auto s = src.p; auto s = src.p;
auto d = dst.p; auto d = dst.p;
const auto d_end = &dst.p[src.count / 2]; const auto d_end = &dst.p[src.count / 2];
uint32_t i, q;
while(d < d_end) { while(d < d_end) {
i = __SXTH(t1, 0); /* 1: I0 */ uint32_t i = __SXTH(t1, 0); /* 1: I0 */
q = __SXTH(t1, 16); /* 1: Q0 */ uint32_t q = __SXTH(t1, 16); /* 1: Q0 */
i = __SMLABB(t2, taps, i); /* 1: I1*3 + I0 */ i = __SMLABB(t2, taps, i); /* 1: I1*3 + I0 */
q = __SMLATB(t2, taps, q); /* 1: Q1*3 + Q0 */ q = __SMLATB(t2, taps, q); /* 1: Q1*3 + Q0 */
t3 = *__SIMD32(s)++; /* 3: Q2:I2 */ const uint32_t t3 = *__SIMD32(s)++; /* 3: Q2:I2 */
t4 = *__SIMD32(s)++; /* Q3:I3 */ const uint32_t t4 = *__SIMD32(s)++; /* Q3:I3 */
i = __SMLABB(t3, taps, i); /* 1: I2*3 + I1*3 + I0 */ i = __SMLABB(t3, taps, i); /* 1: I2*3 + I1*3 + I0 */
q = __SMLATB(t3, taps, q); /* 1: Q2*3 + Q1*3 + Q0 */ q = __SMLATB(t3, taps, q); /* 1: Q2*3 + Q1*3 + Q0 */
@ -645,6 +645,15 @@ buffer_s16_t FIR64AndDecimateBy2Real::execute(
return { dst.p, src.count / 2, src.sampling_rate / 2 }; return { dst.p, src.count / 2, src.sampling_rate / 2 };
} }
void FIRAndDecimateComplex::configure_common(
const size_t taps_count, const size_t decimation_factor
) {
samples_ = std::make_unique<samples_t>(taps_count);
taps_reversed_ = std::make_unique<taps_t>(taps_count);
taps_count_ = taps_count;
decimation_factor_ = decimation_factor;
}
buffer_c16_t FIRAndDecimateComplex::execute( buffer_c16_t FIRAndDecimateComplex::execute(
const buffer_c16_t& src, const buffer_c16_t& src,
const buffer_c16_t& dst const buffer_c16_t& dst

View File

@ -241,12 +241,14 @@ private:
const size_t taps_count, const size_t taps_count,
const size_t decimation_factor const size_t decimation_factor
) { ) {
samples_ = std::make_unique<samples_t>(taps_count); configure_common(taps_count, decimation_factor);
taps_reversed_ = std::make_unique<taps_t>(taps_count);
taps_count_ = taps_count;
decimation_factor_ = decimation_factor;
std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]); std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]);
} }
void configure_common(
const size_t taps_count,
const size_t decimation_factor
);
}; };
class DecimateBy2CIC4Real { class DecimateBy2CIC4Real {

View File

@ -42,8 +42,8 @@ buffer_f32_t AM::execute(
const uint32_t sample1 = *__SIMD32(src_p)++; const uint32_t sample1 = *__SIMD32(src_p)++;
const uint32_t mag_sq0 = __SMUAD(sample0, sample0); const uint32_t mag_sq0 = __SMUAD(sample0, sample0);
const uint32_t mag_sq1 = __SMUAD(sample1, sample1); const uint32_t mag_sq1 = __SMUAD(sample1, sample1);
*(dst_p++) = __builtin_sqrtf(mag_sq0); *(dst_p++) = __builtin_sqrtf(mag_sq0) * k;
*(dst_p++) = __builtin_sqrtf(mag_sq1); *(dst_p++) = __builtin_sqrtf(mag_sq1) * k;
} }
return { dst.p, src.count, src.sampling_rate }; return { dst.p, src.count, src.sampling_rate };
@ -57,10 +57,10 @@ buffer_f32_t SSB::execute(
const auto src_end = &src.p[src.count]; const auto src_end = &src.p[src.count];
auto dst_p = dst.p; auto dst_p = dst.p;
while(src_p < src_end) { while(src_p < src_end) {
*(dst_p++) = (src_p++)->real(); *(dst_p++) = (src_p++)->real() * k;
*(dst_p++) = (src_p++)->real(); *(dst_p++) = (src_p++)->real() * k;
*(dst_p++) = (src_p++)->real(); *(dst_p++) = (src_p++)->real() * k;
*(dst_p++) = (src_p++)->real(); *(dst_p++) = (src_p++)->real() * k;
} }
return { dst.p, src.count, src.sampling_rate }; return { dst.p, src.count, src.sampling_rate };
@ -99,8 +99,8 @@ buffer_f32_t FM::execute(
const auto t0 = multiply_conjugate_s16_s32(s0, z); const auto t0 = multiply_conjugate_s16_s32(s0, z);
const auto t1 = multiply_conjugate_s16_s32(s1, s0); const auto t1 = multiply_conjugate_s16_s32(s1, s0);
z = s1; z = s1;
*(dst_p++) = angle_precise(t0) * k; *(dst_p++) = angle_precise(t0) * kf;
*(dst_p++) = angle_precise(t1) * k; *(dst_p++) = angle_precise(t1) * kf;
} }
z_ = z; z_ = z;
@ -122,9 +122,9 @@ buffer_s16_t FM::execute(
const auto t0 = multiply_conjugate_s16_s32(s0, z); const auto t0 = multiply_conjugate_s16_s32(s0, z);
const auto t1 = multiply_conjugate_s16_s32(s1, s0); const auto t1 = multiply_conjugate_s16_s32(s1, s0);
z = s1; z = s1;
const int32_t theta0_int = angle_approx_0deg27(t0) * k; const int32_t theta0_int = angle_approx_0deg27(t0) * ks16;
const int32_t theta0_sat = __SSAT(theta0_int, 16); const int32_t theta0_sat = __SSAT(theta0_int, 16);
const int32_t theta1_int = angle_approx_0deg27(t1) * k; const int32_t theta1_int = angle_approx_0deg27(t1) * ks16;
const int32_t theta1_sat = __SSAT(theta1_int, 16); const int32_t theta1_sat = __SSAT(theta1_int, 16);
*__SIMD32(dst_p)++ = __PKHBT( *__SIMD32(dst_p)++ = __PKHBT(
theta0_sat, theta0_sat,
@ -137,5 +137,15 @@ buffer_s16_t FM::execute(
return { dst.p, src.count, src.sampling_rate }; return { dst.p, src.count, src.sampling_rate };
} }
void FM::configure(const float sampling_rate, const float deviation_hz) {
/*
* angle: -pi to pi. output range: -32768 to 32767.
* Maximum delta-theta (output of atan2) at maximum deviation frequency:
* delta_theta_max = 2 * pi * deviation / sampling_rate
*/
kf = static_cast<float>(1.0f / (2.0 * pi * deviation_hz / sampling_rate));
ks16 = 32767.0f * kf;
}
} }
} }

View File

@ -33,6 +33,9 @@ public:
const buffer_c16_t& src, const buffer_c16_t& src,
const buffer_f32_t& dst const buffer_f32_t& dst
); );
private:
static constexpr float k = 1.0f / 32768.0f;
}; };
class SSB { class SSB {
@ -41,6 +44,9 @@ public:
const buffer_c16_t& src, const buffer_c16_t& src,
const buffer_f32_t& dst const buffer_f32_t& dst
); );
private:
static constexpr float k = 1.0f / 32768.0f;
}; };
class FM { class FM {
@ -55,18 +61,12 @@ public:
const buffer_s16_t& dst const buffer_s16_t& dst
); );
void configure(const float sampling_rate, const float deviation_hz) { void configure(const float sampling_rate, const float deviation_hz);
/*
* angle: -pi to pi. output range: -32768 to 32767.
* Maximum delta-theta (output of atan2) at maximum deviation frequency:
* delta_theta_max = 2 * pi * deviation / sampling_rate
*/
k = static_cast<float>(32767.0f / (2.0 * pi * deviation_hz / sampling_rate));
}
private: private:
complex16_t::rep_type z_ { 0 }; complex16_t::rep_type z_ { 0 };
float k { 0 }; float kf { 0 };
float ks16 { 0 };
}; };
} /* namespace demodulate */ } /* namespace demodulate */

View File

@ -86,11 +86,9 @@ void EventDispatcher::dispatch(const eventmask_t events) {
} }
void EventDispatcher::handle_baseband_queue() { void EventDispatcher::handle_baseband_queue() {
std::array<uint8_t, Message::MAX_SIZE> message_buffer; shared_memory.baseband_queue.handle([this](Message* const message) {
while(Message* const message = shared_memory.baseband_queue.peek(message_buffer)) { this->on_message(message);
on_message(message); });
shared_memory.baseband_queue.skip();
}
} }
void EventDispatcher::on_message(const Message* const message) { void EventDispatcher::on_message(const Message* const message) {

View File

@ -261,7 +261,7 @@
* lower priority, this may slow down the driver a bit however. * lower priority, this may slow down the driver a bit however.
*/ */
#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__) #if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
#define SDC_NICE_WAITING TRUE #define SDC_NICE_WAITING FALSE
#endif #endif
/*===========================================================================*/ /*===========================================================================*/

View File

@ -42,7 +42,6 @@
#include "debug.hpp" #include "debug.hpp"
#include "audio.hpp"
#include "audio_dma.hpp" #include "audio_dma.hpp"
#include "gcc.hpp" #include "gcc.hpp"
@ -73,19 +72,10 @@ void __late_init(void) {
} }
static void init() { static void init() {
i2s::i2s0::configure(
audio::i2s0_config_tx,
audio::i2s0_config_rx,
audio::i2s0_config_dma
);
audio::dma::init(); audio::dma::init();
audio::dma::configure(); audio::dma::configure();
audio::dma::enable(); audio::dma::enable();
i2s::i2s0::tx_start();
i2s::i2s0::rx_start();
LPC_CREG->DMAMUX = portapack::gpdma_mux; LPC_CREG->DMAMUX = portapack::gpdma_mux;
gpdma::controller.enable(); gpdma::controller.enable();
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY)); nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));

View File

@ -38,6 +38,7 @@ void MatchedFilter::configure(
taps_reversed_ = std::make_unique<taps_t>(taps_count); taps_reversed_ = std::make_unique<taps_t>(taps_count);
taps_count_ = taps_count; taps_count_ = taps_count;
decimation_factor_ = decimation_factor; decimation_factor_ = decimation_factor;
output = 0;
std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]); std::reverse_copy(&taps[0], &taps[taps_count], &taps_reversed_[0]);
} }

View File

@ -71,7 +71,7 @@ private:
size_t taps_count_ { 0 }; size_t taps_count_ { 0 };
size_t decimation_factor_ { 1 }; size_t decimation_factor_ { 1 };
size_t decimation_phase { 0 }; size_t decimation_phase { 0 };
float output; float output { 0 };
void shift_by_decimation_factor(); void shift_by_decimation_factor();

93
firmware/baseband/ook.hpp Normal file
View File

@ -0,0 +1,93 @@
/*
* 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 __OOK_HPP__
#define __OOK_HPP__
#include "phase_detector.hpp"
#include "phase_accumulator.hpp"
#include <cstdint>
#include <complex>
#include <algorithm>
#include <cmath>
class OOKSlicerMagSquaredInt {
public:
using symbol_t = bool;
constexpr OOKSlicerMagSquaredInt(
const float samples_per_symbol
) : mag2_threshold_leak_factor {
static_cast<uint32_t>(
factor_sq(-1.0f / (8.0f * samples_per_symbol)) * float(1ULL << 32)
)
}
{
}
symbol_t operator()(const std::complex<int16_t> in) {
const uint32_t real2 = in.real() * in.real();
const uint32_t imag2 = in.imag() * in.imag();
const uint32_t mag2 = real2 + imag2;
const uint32_t mag2_attenuated = mag2 >> 3; // Approximation of (-4.5dB)^2
mag2_threshold = (uint64_t(mag2_threshold) * uint64_t(mag2_threshold_leak_factor)) >> 32;
mag2_threshold = std::max(mag2_threshold, mag2_attenuated);
const bool symbol = (mag2 > mag2_threshold);
return symbol;
}
private:
const uint32_t mag2_threshold_leak_factor;
uint32_t mag2_threshold = 0;
constexpr float factor_sq(float db) {
return std::pow(10.0f, db / (10.0f / 2));
}
};
class OOKClockRecovery {
public:
constexpr OOKClockRecovery(
const float samples_per_symbol
) : symbol_phase_inc_nominal { static_cast<uint32_t>(std::round((1ULL << 32) / samples_per_symbol)) },
phase_detector { samples_per_symbol },
phase_accumulator { symbol_phase_inc_nominal }
{
}
template<typename SymbolHandler>
void operator()(const uint32_t slicer_history, SymbolHandler symbol_handler) {
if( phase_accumulator() ) {
const auto detector_result = phase_detector(slicer_history);
phase_accumulator.set_inc(symbol_phase_inc_nominal + detector_result.error * (symbol_phase_inc_nominal >> 3));
symbol_handler(detector_result.symbol);
}
}
private:
const uint32_t symbol_phase_inc_nominal;
PhaseDetectorEarlyLateGate phase_detector;
PhaseAccumulator phase_accumulator;
};
#endif/*__OOK_HPP__*/

View File

@ -53,8 +53,8 @@ public:
const PreambleMatcher preamble_matcher, const PreambleMatcher preamble_matcher,
const UnstuffMatcher unstuff_matcher, const UnstuffMatcher unstuff_matcher,
const EndMatcher end_matcher, const EndMatcher end_matcher,
const PayloadHandlerFunc payload_handler PayloadHandlerFunc payload_handler
) : payload_handler { payload_handler }, ) : payload_handler { std::move(payload_handler) },
preamble(preamble_matcher), preamble(preamble_matcher),
unstuff(unstuff_matcher), unstuff(unstuff_matcher),
end(end_matcher) end(end_matcher)
@ -89,8 +89,13 @@ public:
} }
if( end(bit_history, packet.size()) ) { if( end(bit_history, packet.size()) ) {
packet.set_timestamp(Timestamp::now()); // NOTE: This check is to avoid std::function nullptr check, which
payload_handler(packet); // brings in "_ZSt25__throw_bad_function_callv" and a lot of extra code.
// TODO: Make payload_handler known at compile time.
if( payload_handler ) {
packet.set_timestamp(Timestamp::now());
payload_handler(packet);
}
reset_state(); reset_state();
} else { } else {
if( packet_truncated() ) { if( packet_truncated() ) {

View File

@ -0,0 +1,50 @@
/*
* 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 __PHASE_ACCUMULATOR_HPP__
#define __PHASE_ACCUMULATOR_HPP__
#include <cstdint>
class PhaseAccumulator {
public:
constexpr PhaseAccumulator(
const uint32_t phase_inc
) : phase_inc { phase_inc }
{
}
bool operator()() {
const auto last_phase = phase;
phase += phase_inc;
return (phase < last_phase);
}
void set_inc(const uint32_t new_phase_inc) {
phase_inc = new_phase_inc;
}
private:
uint32_t phase { 0 };
uint32_t phase_inc;
};
#endif/*__PHASE_ACCUMULATOR_HPP__*/

View File

@ -0,0 +1,68 @@
/*
* 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 __PHASE_DETECTOR_HPP__
#define __PHASE_DETECTOR_HPP__
#include <cstdint>
#include <cstddef>
#include <cmath>
class PhaseDetectorEarlyLateGate {
public:
using history_t = uint32_t;
using symbol_t = bool;
using error_t = int;
struct result_t {
symbol_t symbol;
error_t error;
};
constexpr PhaseDetectorEarlyLateGate(
const float samples_per_symbol
) : late_mask { (1U << static_cast<size_t>(std::ceil(samples_per_symbol / 2))) - 1 },
early_mask { late_mask << static_cast<size_t>(std::floor(samples_per_symbol / 2)) },
sample_bit { static_cast<size_t>(std::floor(samples_per_symbol / 2)) }
{
}
result_t operator()(const history_t symbol_history) const {
// history = ...0111, early
// history = ...1110, late
const symbol_t symbol = (symbol_history >> sample_bit) & 1;
const int late_side = __builtin_popcount(symbol_history & late_mask);
const int early_side = __builtin_popcount(symbol_history & early_mask);
const int lateness = late_side - early_side;
const int direction = lateness; //std::min(std::max(lateness, -1), 1);
const error_t error = direction;
return { symbol, error };
}
private:
const history_t late_mask;
const history_t early_mask;
const size_t sample_bit;
};
#endif/*__PHASE_DETECTOR_HPP__*/

View File

@ -40,6 +40,7 @@ void NarrowbandAMAudio::execute(const buffer_c8_t& buffer) {
channel_spectrum.feed(channel_out, channel_filter_pass_f, channel_filter_stop_f); channel_spectrum.feed(channel_out, channel_filter_pass_f, channel_filter_stop_f);
auto audio = demodulate(channel_out); auto audio = demodulate(channel_out);
audio_compressor.execute_in_place(audio);
audio_output.write(audio); audio_output.write(audio);
} }
@ -86,7 +87,7 @@ void NarrowbandAMAudio::configure(const AMConfigureMessage& message) {
channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor); channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor);
channel_filter_pass_f = message.channel_filter.pass_frequency_normalized * channel_filter_input_fs; channel_filter_pass_f = message.channel_filter.pass_frequency_normalized * channel_filter_input_fs;
channel_filter_stop_f = message.channel_filter.stop_frequency_normalized * channel_filter_input_fs; channel_filter_stop_f = message.channel_filter.stop_frequency_normalized * channel_filter_input_fs;
channel_spectrum.set_decimation_factor(std::floor((channel_filter_output_fs / 2) / ((channel_filter_pass_f + channel_filter_stop_f) / 2))); channel_spectrum.set_decimation_factor(std::floor(channel_filter_output_fs / (channel_filter_pass_f + channel_filter_stop_f)));
modulation_ssb = (message.modulation == AMConfigureMessage::Modulation::SSB); modulation_ssb = (message.modulation == AMConfigureMessage::Modulation::SSB);
audio_output.configure(message.audio_hpf_config); audio_output.configure(message.audio_hpf_config);

View File

@ -26,6 +26,7 @@
#include "dsp_decimate.hpp" #include "dsp_decimate.hpp"
#include "dsp_demodulate.hpp" #include "dsp_demodulate.hpp"
#include "audio_compressor.hpp"
#include "audio_output.hpp" #include "audio_output.hpp"
#include "spectrum_collector.hpp" #include "spectrum_collector.hpp"
@ -64,7 +65,7 @@ private:
bool modulation_ssb = false; bool modulation_ssb = false;
dsp::demodulate::AM demod_am; dsp::demodulate::AM demod_am;
dsp::demodulate::SSB demod_ssb; dsp::demodulate::SSB demod_ssb;
FeedForwardCompressor audio_compressor;
AudioOutput audio_output; AudioOutput audio_output;
SpectrumCollector channel_spectrum; SpectrumCollector channel_spectrum;

View File

@ -0,0 +1,87 @@
/*
* 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 "proc_capture.hpp"
#include "dsp_fir_taps.hpp"
#include "utility.hpp"
CaptureProcessor::CaptureProcessor() {
const auto& decim_0_filter = taps_200k_decim_0;
constexpr size_t decim_0_input_fs = baseband_fs;
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
const auto& decim_1_filter = taps_200k_decim_1;
constexpr size_t decim_1_input_fs = decim_0_output_fs;
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
const auto& channel_filter = decim_1_filter;
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
constexpr size_t channel_decimation = 1;
const size_t channel_filter_output_fs = channel_filter_input_fs / channel_decimation;
decim_0.configure(decim_0_filter.taps, 33554432);
decim_1.configure(decim_1_filter.taps, 131072);
channel_filter_pass_f = channel_filter.pass_frequency_normalized * channel_filter_input_fs;
channel_filter_stop_f = channel_filter.stop_frequency_normalized * channel_filter_input_fs;
spectrum_interval_samples = channel_filter_output_fs / spectrum_rate_hz;
spectrum_samples = 0;
channel_spectrum.set_decimation_factor(1);
stream = std::make_unique<StreamInput>(15);
}
void CaptureProcessor::execute(const buffer_c8_t& buffer) {
/* 2.4576MHz, 2048 samples */
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto& decimator_out = decim_1_out;
const auto& channel = decimator_out;
if( stream ) {
const size_t bytes_to_write = sizeof(*decimator_out.p) * decimator_out.count;
const auto result = stream->write(decimator_out.p, bytes_to_write);
}
feed_channel_stats(channel);
spectrum_samples += channel.count;
if( spectrum_samples >= spectrum_interval_samples ) {
spectrum_samples -= spectrum_interval_samples;
channel_spectrum.feed(channel, channel_filter_pass_f, channel_filter_stop_f);
}
}
void CaptureProcessor::on_message(const Message* const message) {
switch(message->id) {
case Message::ID::UpdateSpectrum:
case Message::ID::SpectrumStreamingConfig:
channel_spectrum.on_message(message);
break;
default:
break;
}
}

View File

@ -0,0 +1,65 @@
/*
* 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 __PROC_CAPTURE_HPP__
#define __PROC_CAPTURE_HPP__
#include "baseband_processor.hpp"
#include "dsp_decimate.hpp"
#include "spectrum_collector.hpp"
#include "stream_input.hpp"
#include <array>
#include <memory>
class CaptureProcessor : public BasebandProcessor {
public:
CaptureProcessor();
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 2457600;
static constexpr auto spectrum_rate_hz = 50.0f;
std::array<complex16_t, 512> dst;
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0;
dsp::decimate::FIRC16xR16x16Decim2 decim_1;
uint32_t channel_filter_pass_f = 0;
uint32_t channel_filter_stop_f = 0;
std::unique_ptr<StreamInput> stream;
SpectrumCollector channel_spectrum;
size_t spectrum_interval_samples = 0;
size_t spectrum_samples = 0;
};
#endif/*__PROC_CAPTURE_HPP__*/

View File

@ -76,8 +76,8 @@ void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) {
demod.configure(demod_input_fs, message.deviation); demod.configure(demod_input_fs, message.deviation);
channel_filter_pass_f = message.channel_filter.pass_frequency_normalized * channel_filter_input_fs; channel_filter_pass_f = message.channel_filter.pass_frequency_normalized * channel_filter_input_fs;
channel_filter_stop_f = message.channel_filter.stop_frequency_normalized * channel_filter_input_fs; channel_filter_stop_f = message.channel_filter.stop_frequency_normalized * channel_filter_input_fs;
channel_spectrum.set_decimation_factor(std::floor((channel_filter_output_fs / 2) / ((channel_filter_pass_f + channel_filter_stop_f) / 2))); channel_spectrum.set_decimation_factor(std::floor(channel_filter_output_fs / (channel_filter_pass_f + channel_filter_stop_f)));
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, 12288); audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, 0.5f);
configured = true; configured = true;
} }

View File

@ -21,31 +21,8 @@
#include "proc_tpms.hpp" #include "proc_tpms.hpp"
#include "portapack_shared_memory.hpp"
#include "dsp_fir_taps.hpp" #include "dsp_fir_taps.hpp"
// IFIR image-reject filter: fs=2457600, pass=100000, stop=407200, decim=4, fout=614400
static constexpr fir_taps_real<24> taps_200k_decim_0 = {
.pass_frequency_normalized = 100000.0f / 2457600.0f,
.stop_frequency_normalized = 407200.0f / 2457600.0f,
.taps = { {
90, 94, 4, -240, -570, -776, -563, 309,
1861, 3808, 5618, 6710, 6710, 5618, 3808, 1861,
309, -563, -776, -570, -240, 4, 94, 90,
} },
};
// IFIR prototype filter: fs=614400, pass=100000, stop=207200, decim=2, fout=307200
static constexpr fir_taps_real<16> taps_200k_decim_1 = {
.pass_frequency_normalized = 100000.0f / 614400.0f,
.stop_frequency_normalized = 207200.0f / 614400.0f,
.taps = { {
-132, -256, 545, 834, -1507, -2401, 4666, 14583,
14583, 4666, -2401, -1507, 834, 545, -256, -132,
} },
};
TPMSProcessor::TPMSProcessor() { TPMSProcessor::TPMSProcessor() {
decim_0.configure(taps_200k_decim_0.taps, 33554432); decim_0.configure(taps_200k_decim_0.taps, 33554432);
decim_1.configure(taps_200k_decim_1.taps, 131072); decim_1.configure(taps_200k_decim_1.taps, 131072);
@ -66,6 +43,18 @@ void TPMSProcessor::execute(const buffer_c8_t& buffer) {
clock_recovery(mf.get_output()); clock_recovery(mf.get_output());
} }
} }
for(size_t i=0; i<decim_1_out.count; i+=channel_decimation) {
const auto sliced = ook_slicer_5sps(decim_1_out.p[i]);
slicer_history = (slicer_history << 1) | sliced;
ook_clock_recovery_subaru(slicer_history, [this](const bool symbol) {
this->packet_builder_ook_subaru.execute(symbol);
});
ook_clock_recovery_gmc(slicer_history, [this](const bool symbol) {
this->packet_builder_ook_gmc.execute(symbol);
});
}
} }
void TPMSProcessor::consume_symbol( void TPMSProcessor::consume_symbol(
@ -78,6 +67,6 @@ void TPMSProcessor::consume_symbol(
void TPMSProcessor::payload_handler( void TPMSProcessor::payload_handler(
const baseband::Packet& packet const baseband::Packet& packet
) { ) {
const TPMSPacketMessage message { packet }; const TPMSPacketMessage message { tpms::SignalType::FLM, packet };
shared_memory.application_queue.push(message); shared_memory.application_queue.push(message);
} }

View File

@ -32,7 +32,10 @@
#include "packet_builder.hpp" #include "packet_builder.hpp"
#include "baseband_packet.hpp" #include "baseband_packet.hpp"
#include "ook.hpp"
#include "message.hpp" #include "message.hpp"
#include "portapack_shared_memory.hpp"
#include <cstdint> #include <cstdint>
#include <cstddef> #include <cstddef>
@ -83,6 +86,38 @@ private:
} }
}; };
static constexpr float channel_rate_in = 307200.0f;
static constexpr size_t channel_decimation = 8;
static constexpr float channel_sample_rate = channel_rate_in / channel_decimation;
OOKSlicerMagSquaredInt ook_slicer_5sps { 5 };
uint32_t slicer_history { 0 };
OOKClockRecovery ook_clock_recovery_subaru {
channel_sample_rate / 8192.0f
};
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_ook_subaru {
{ 0b010101010101010101011110, 24, 0 },
{ },
{ 80 },
[](const baseband::Packet& packet) {
const TPMSPacketMessage message { tpms::SignalType::Subaru, packet };
shared_memory.application_queue.push(message);
}
};
OOKClockRecovery ook_clock_recovery_gmc {
channel_sample_rate / 8400.0f
};
PacketBuilder<BitPattern, NeverMatch, FixedLength> packet_builder_ook_gmc {
{ 0b01010101010101010101010101100101, 32, 0 },
{ },
{ 192 },
[](const baseband::Packet& packet) {
const TPMSPacketMessage message { tpms::SignalType::GMC, packet };
shared_memory.application_queue.push(message);
}
};
void consume_symbol(const float symbol); void consume_symbol(const float symbol);
void payload_handler(const baseband::Packet& packet); void payload_handler(const baseband::Packet& packet);
}; };

View File

@ -33,6 +33,8 @@ using namespace lpc43xx;
#include "portapack_dma.hpp" #include "portapack_dma.hpp"
#include "portapack_adc.hpp" #include "portapack_adc.hpp"
#include "thread_wait.hpp"
namespace rf { namespace rf {
namespace rssi { namespace rssi {
namespace dma { namespace dma {
@ -99,20 +101,19 @@ static buffers_config_t buffers_config;
static sample_t *samples { nullptr }; static sample_t *samples { nullptr };
static gpdma::channel::LLI *lli { nullptr }; static gpdma::channel::LLI *lli { nullptr };
static Semaphore semaphore; static ThreadWait thread_wait;
static volatile const gpdma::channel::LLI* next_lli = nullptr;
static void transfer_complete() { static void transfer_complete() {
next_lli = gpdma_channel.next_lli(); const auto next_lli_index = gpdma_channel.next_lli() - &lli[0];
chSemSignalI(&semaphore); thread_wait.wake_from_interrupt(next_lli_index);
} }
static void dma_error() { static void dma_error() {
thread_wait.wake_from_interrupt(-1);
disable(); disable();
} }
void init() { void init() {
chSemInit(&semaphore, 0);
gpdma_channel.set_handlers(transfer_complete, dma_error); gpdma_channel.set_handlers(transfer_complete, dma_error);
// LPC_GPDMA->SYNC |= (1 << gpdma_peripheral); // LPC_GPDMA->SYNC |= (1 << gpdma_peripheral);
@ -147,8 +148,6 @@ void free() {
void enable() { void enable() {
const auto gpdma_config = config(); const auto gpdma_config = config();
gpdma_channel.configure(lli[0], gpdma_config); gpdma_channel.configure(lli[0], gpdma_config);
chSemReset(&semaphore, 0);
gpdma_channel.enable(); gpdma_channel.enable();
} }
@ -161,16 +160,11 @@ void disable() {
} }
rf::rssi::buffer_t wait_for_buffer() { rf::rssi::buffer_t wait_for_buffer() {
const auto status = chSemWait(&semaphore); const auto next_index = thread_wait.sleep();
if( status == RDY_OK ) {
const auto next = next_lli; if( next_index >= 0 ) {
if( next ) { const size_t free_index = (next_index + buffers_config.count - 2) % buffers_config.count;
const size_t next_index = next - &lli[0]; return { reinterpret_cast<sample_t*>(lli[free_index].destaddr), buffers_config.items_per_buffer };
const size_t free_index = (next_index + buffers_config.count - 2) % buffers_config.count;
return { reinterpret_cast<sample_t*>(lli[free_index].destaddr), buffers_config.items_per_buffer };
} else {
return { nullptr, 0 };
}
} else { } else {
// TODO: Should I return here, or loop if RDY_RESET? // TODO: Should I return here, or loop if RDY_RESET?
return { nullptr, 0 }; return { nullptr, 0 };

View File

@ -30,11 +30,6 @@
class RSSIThread : public ThreadBase { class RSSIThread : public ThreadBase {
public: public:
RSSIThread(
) : ThreadBase { "rssi" }
{
}
Thread* start(const tprio_t priority); Thread* start(const tprio_t priority);
private: private:

View File

@ -117,8 +117,8 @@ void SpectrumCollector::update() {
// Three point Hamming window. // Three point Hamming window.
const auto corrected_sample = channel_spectrum[i] * 0.54f const auto corrected_sample = channel_spectrum[i] * 0.54f
+ (channel_spectrum[(i-1) & 0xff] + channel_spectrum[(i+1) & 0xff]) * -0.23f; + (channel_spectrum[(i-1) & 0xff] + channel_spectrum[(i+1) & 0xff]) * -0.23f;
const auto mag2 = magnitude_squared(corrected_sample); const auto mag2 = magnitude_squared(corrected_sample * (1.0f / 32768.0f));
const float db = complex16_mag_squared_to_dbv_norm(mag2); const float db = mag2_to_dbv_norm(mag2);
constexpr float mag_scale = 5.0f; constexpr float mag_scale = 5.0f;
const unsigned int v = (db * mag_scale) + 255.0f; const unsigned int v = (db * mag_scale) + 255.0f;
spectrum.db[i] = std::max(0U, std::min(255U, v)); spectrum.db[i] = std::max(0U, std::min(255U, v));

View File

@ -35,7 +35,8 @@
class SpectrumCollector { class SpectrumCollector {
public: public:
constexpr SpectrumCollector( constexpr SpectrumCollector(
) : channel_spectrum_decimator { 1 } ) : channel_spectrum_decimator { 1 },
fifo { fifo_data, ChannelSpectrumConfigMessage::fifo_k }
{ {
} }
@ -52,6 +53,7 @@ public:
private: private:
BlockDecimator<complex16_t, 256> channel_spectrum_decimator; BlockDecimator<complex16_t, 256> channel_spectrum_decimator;
ChannelSpectrumFIFO fifo; ChannelSpectrumFIFO fifo;
ChannelSpectrum fifo_data[1 << ChannelSpectrumConfigMessage::fifo_k];
volatile bool channel_spectrum_request_update { false }; volatile bool channel_spectrum_request_update { false };
bool streaming { false }; bool streaming { false };

View File

@ -0,0 +1,73 @@
/*
* 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 __STREAM_INPUT_H__
#define __STREAM_INPUT_H__
#include "portapack_shared_memory.hpp"
#include "fifo.hpp"
#include <cstdint>
#include <cstddef>
#include <memory>
class StreamInput {
public:
StreamInput(const size_t K) :
K { K },
data { std::make_unique<uint8_t[]>(1UL << K) },
fifo { data.get(), K }
{
// TODO: Send stream creation message.
shared_memory.FIFO_HACK = &fifo;
}
~StreamInput() {
// TODO: Send stream distruction message.
shared_memory.FIFO_HACK = nullptr;
}
size_t write(const void* const data, const size_t length) {
const auto written = fifo.in(reinterpret_cast<const uint8_t*>(data), length);
const auto last_bytes_written = bytes_written;
bytes_written += written;
if( (bytes_written & event_bytes_mask) < (last_bytes_written & event_bytes_mask) ) {
creg::m4txevent::assert();
}
return written;
}
uint64_t written() const {
return bytes_written;
}
private:
const size_t K;
const uint64_t event_bytes_mask = (1ULL << (K - 2)) - 1;
uint64_t bytes_written = 0;
std::unique_ptr<uint8_t[]> data;
FIFO<uint8_t> fifo;
};
#endif/*__STREAM_INPUT_H__*/

View File

@ -26,24 +26,17 @@
class ThreadBase { class ThreadBase {
public: public:
constexpr ThreadBase( virtual ~ThreadBase() = default;
const char* const name
) : name { name }
{
}
protected: protected:
static msg_t fn(void* arg) { static msg_t fn(void* arg) {
auto obj = static_cast<ThreadBase*>(arg); auto obj = static_cast<ThreadBase*>(arg);
chRegSetThreadName(obj->name);
obj->run(); obj->run();
return 0; return 0;
} }
private: private:
const char* const name;
virtual void run() = 0; virtual void run() = 0;
}; };

View File

@ -19,14 +19,14 @@
<div class="abst"> <div class="abst">
<img src="img/layers.png" class="rset" width="245" height="255" alt="layer"> <img src="img/layers.png" class="rset" width="245" height="255" alt="layer">
<p>FatFs is a generic FAT file system module for small embedded systems. The FatFs module is written in compliance with ANSI C (C89) and completely separated from the disk I/O layer. Therefore it is independent of the platform. It can be incorporated into small microcontrollers with limited resource, such as 8051, PIC, AVR, ARM, Z80, 78K and etc. Also Petit FatFs module for tiny microcontrollers is available <a href="http://elm-chan.org/fsw/ff/00index_p.html">here</a>.</p> <p>FatFs is a generic FAT file system module for small embedded systems. The FatFs module is written in compliance with ANSI C (C89) and completely separated from the disk I/O layer. Therefore it is independent of the platform. It can be incorporated into small microcontrollers with limited resource, such as 8051, PIC, AVR, ARM, Z80, 78K and etc. Also Petit FatFs module for tiny microcontrollers is available <a href="http://elm-chan.org/fsw/ff/00index_p.html">here</a>.</p>
<h4>Features</h4> <h4>Features</h4>
<ul> <ul>
<li>Windows compatible FAT file system.</li> <li>Windows compatible FAT file system.</li>
<li>Platform independent. Easy to port.</li> <li>Platform independent. Easy to port.</li>
<li>Very small footprint for code and work area.</li> <li>Very small footprint for code and work area.</li>
<li>Various configuration options: <li>Various <a href="en/config.html">configuration options</a>:
<ul> <ul>
<li>Multiple volumes (physical drives and partitions).</li> <li>Multiple volumes (physical drives and partitions).</li>
<li>Multiple ANSI/OEM code pages including DBCS.</li> <li>Multiple ANSI/OEM code pages including DBCS.</li>
@ -42,49 +42,68 @@
<div class="para"> <div class="para">
<h3>Application Interface</h3> <h3>Application Interface</h3>
<p>FatFs module provides following functions to the applications. In other words, this list describes what FatFs can do to access the FAT volumes.</p> <img src="img/layers1.png" class="rset" width="245" height="220" alt="layer">
<ul> <ul>
<li><a href="en/mount.html">f_mount</a> - Register/Unregister a work area</li> <li>File Access
<li><a href="en/open.html">f_open</a> - Open/Create a file</li> <ul>
<li><a href="en/close.html">f_close</a> - Close an open file</li> <li><a href="en/open.html">f_open</a> - Open/Create a file</li>
<li><a href="en/read.html">f_read</a> - Read file</li> <li><a href="en/close.html">f_close</a> - Close an open file</li>
<li><a href="en/write.html">f_write</a> - Write file</li> <li><a href="en/read.html">f_read</a> - Read data</li>
<li><a href="en/lseek.html">f_lseek</a> - Move read/write pointer, Expand file size</li> <li><a href="en/write.html">f_write</a> - Write data</li>
<li><a href="en/truncate.html">f_truncate</a> - Truncate file size</li> <li><a href="en/lseek.html">f_lseek</a> - Move read/write pointer, Expand size</li>
<li><a href="en/sync.html">f_sync</a> - Flush cached data</li> <li><a href="en/truncate.html">f_truncate</a> - Truncate size</li>
<li><a href="en/forward.html">f_forward</a> - Forward file data to the stream</li> <li><a href="en/sync.html">f_sync</a> - Flush cached data</li>
<li><a href="en/stat.html">f_stat</a> - Check existance of a file or sub-directory</li> <li><a href="en/forward.html">f_forward</a> - Forward data to the stream</li>
<li><a href="en/opendir.html">f_opendir</a> - Open a directory</li> <li><a href="en/gets.html">f_gets</a> - Read a string</li>
<li><a href="en/closedir.html">f_closedir</a> - Close an open directory</li> <li><a href="en/putc.html">f_putc</a> - Write a character</li>
<li><a href="en/readdir.html">f_readdir</a> - Read a directory item</li> <li><a href="en/puts.html">f_puts</a> - Write a string</li>
<li><a href="en/mkdir.html">f_mkdir</a> - Create a sub-directory</li> <li><a href="en/printf.html">f_printf</a> - Write a formatted string</li>
<li><a href="en/unlink.html">f_unlink</a> - Remove a file or sub-directory</li> <li><a href="en/tell.html">f_tell</a> - Get current read/write pointer</li>
<li><a href="en/chmod.html">f_chmod</a> - Change attribute</li> <li><a href="en/eof.html">f_eof</a> - Test for end-of-file</li>
<li><a href="en/utime.html">f_utime</a> - Change timestamp</li> <li><a href="en/size.html">f_size</a> - Get size</li>
<li><a href="en/rename.html">f_rename</a> - Rename/Move a file or sub-directory</li> <li><a href="en/error.html">f_error</a> - Test for an error</li>
<li><a href="en/chdir.html">f_chdir</a> - Change current directory</li> </ul>
<li><a href="en/chdrive.html">f_chdrive</a> - Change current drive</li> </li>
<li><a href="en/getcwd.html">f_getcwd</a> - Retrieve the current directory</li> <li>Directory Access
<li><a href="en/getfree.html">f_getfree</a> - Get free space on the volume</li> <ul>
<li><a href="en/getlabel.html">f_getlabel</a> - Get volume label</li> <li><a href="en/opendir.html">f_opendir</a> - Open a directory</li>
<li><a href="en/setlabel.html">f_setlabel</a> - Set volume label</li> <li><a href="en/closedir.html">f_closedir</a> - Close an open directory</li>
<li><a href="en/mkfs.html">f_mkfs</a> - Create a file system on the drive</li> <li><a href="en/readdir.html">f_readdir</a> - Read an item</li>
<li><a href="en/fdisk.html">f_fdisk</a> - Divide a physical drive</li> <li><a href="en/findfirst.html">f_findfirst</a> - Open a directory and read first item found</li>
<li><a href="en/gets.html">f_gets</a> - Read a string</li> <li><a href="en/findnext.html">f_findnext</a> - Read a next item found</li>
<li><a href="en/putc.html">f_putc</a> - Write a character</li> </ul>
<li><a href="en/puts.html">f_puts</a> - Write a string</li> </li>
<li><a href="en/printf.html">f_printf</a> - Write a formatted string</li> <li>File/Directory Management
<li><a href="en/tell.html">f_tell</a> - Get current read/write pointer</li> <ul>
<li><a href="en/eof.html">f_eof</a> - Test for end-of-file on a file</li> <li><a href="en/stat.html">f_stat</a> - Check existance of a file or sub-directory</li>
<li><a href="en/size.html">f_size</a> - Get size of a file</li> <li><a href="en/unlink.html">f_unlink</a> - Remove a file or sub-directory</li>
<li><a href="en/error.html">f_error</a> - Test for an error on a file</li> <li><a href="en/rename.html">f_rename</a> - Rename or move a file or sub-directory</li>
<li><a href="en/chmod.html">f_chmod</a> - Change attribute of a file or sub-directory</li>
<li><a href="en/utime.html">f_utime</a> - Change timestamp of a file or sub-directory</li>
<li><a href="en/mkdir.html">f_mkdir</a> - Create a sub-directory</li>
<li><a href="en/chdir.html">f_chdir</a> - Change current directory</li>
<li><a href="en/chdrive.html">f_chdrive</a> - Change current drive</li>
<li><a href="en/getcwd.html">f_getcwd</a> - Retrieve the current directory and drive</li>
</ul>
</li>
<li>Volume Management
<ul>
<li><a href="en/mount.html">f_mount</a> - Register/Unregister a work area of a volume</li>
<li><a href="en/mkfs.html">f_mkfs</a> - Create an FAT volume on the logical drive</li>
<li><a href="en/fdisk.html">f_fdisk</a> - Create logical drives on the physical drive</li>
<li><a href="en/getfree.html">f_getfree</a> - Get total size and free size on the volume</li>
<li><a href="en/getlabel.html">f_getlabel</a> - Get volume label</li>
<li><a href="en/setlabel.html">f_setlabel</a> - Set volume label</li>
</ul>
</li>
</ul> </ul>
</div> </div>
<div class="para"> <div class="para">
<h3>Device Control Interface</h3> <h3>Device Control Interface</h3>
<p>Since the FatFs module is a file system layer, it is completely separated from physical devices, such as memory card, harddisk and any type of storage devices. FatFs accesses the storage device via a simple interface described below. The low level device control module is not a part of FatFs module. It is provided by implementer. Also sample implementations for some platforms are available in the downloads.</p> <img src="img/layers2.png" class="rset" width="245" height="220" alt="layer">
<p>Since the FatFs module is a file system layer, it is completely separated from the physical devices, such as memory card, harddisk and any type of storage devices. FatFs accesses the storage devices via a simple interface shown below. The low level device control module is not a part of FatFs module. It is provided by implementer. Also sample implementations for some platforms are available in the downloads.</p>
<ul> <ul>
<li><a href="en/dstat.html">disk_status</a> - Get device status</li> <li><a href="en/dstat.html">disk_status</a> - Get device status</li>
<li><a href="en/dinit.html">disk_initialize</a> - Initialize device</li> <li><a href="en/dinit.html">disk_initialize</a> - Initialize device</li>
@ -100,17 +119,20 @@
<h3>Resources</h3> <h3>Resources</h3>
<p>The FatFs module is a free software opened for education, research and development. You can use, modify and/or redistribute it for personal projects or commercial products without any restriction under your responsibility. For further information, refer to the application note.</p> <p>The FatFs module is a free software opened for education, research and development. You can use, modify and/or redistribute it for personal projects or commercial products without any restriction under your responsibility. For further information, refer to the application note.</p>
<ul> <ul>
<li><a href="http://elm-chan.org/fsw/ff/00index_e.html"><em>FatFs Home Page</em></a></li> <li>Read first: <a href="en/appnote.html">FatFs module application note</a> <span class="mfd">March 18, 2015</span></li>
<li><a href="http://elm-chan.org/fsw/ff/bd/"><em>FatFs User Forum</em></a></li> <li>Download: <a href="ff11.zip">FatFs R0.11</a> | <a href="updates.txt">Updates</a> | <a href="patches.html">Patches</a> <span class="mfd">March 9, 2015</span></li>
<li>Read first: <a href="en/appnote.html">FatFs module application note</a></li> <li>Download: <a href="ffsample.zip">FatFs sample projects for various platforms</a> <span class="mfd">February 9, 2015</span></li>
<li>Download: <a href="archives.html">Old Releases</a></li>
<li>Community: <a href="http://elm-chan.org/fsw/ff/bd/">FatFs User Forum</a></li>
<li><a href="http://stm32f4-discovery.com/2014/07/library-21-read-sd-card-fatfs-stm32f4xx-devices/">Read SD card with FatFs on STM32F4xx devices by Tilen Majerle</a>↗ (Quick and easy implementation for STM32F4-Discovery)</li> <li><a href="http://stm32f4-discovery.com/2014/07/library-21-read-sd-card-fatfs-stm32f4xx-devices/">Read SD card with FatFs on STM32F4xx devices by Tilen Majerle</a>↗ (Quick and easy implementation for STM32F4-Discovery)</li>
<li><a href="http://nemuisan.blog.bai.ne.jp/">Nemuisan's Blog</a>↗ (Well written implementations for STM32F/SDIO and LPC2300/MCI)</li> <li><a href="http://nemuisan.blog.bai.ne.jp/">Nemuisan's Blog</a>↗ (Well written implementations for STM32F/SDIO and LPC2300/MCI)</li>
<li><a href="http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/arm_memcards/index.html">ARM-Projects by Martin THOMAS</a>↗ (Examples for LPC2000, AT91SAM and STM32)</li> <li><a href="http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/arm_memcards/index.html">ARM-Projects by Martin THOMAS</a>↗ (Examples for LPC2000, AT91SAM and STM32)</li>
<li><a href="http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx">FAT32 Specification by Microsoft</a>↗ (The authorized document on FAT file system)</li> <li><a href="http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx">FAT32 Specification by Microsoft</a>↗ (The authorized document on FAT file system)</li>
<li><a href="http://elm-chan.org/docs/fat.html">The basics of FAT file system [ja]</a></li> <li><a href="http://elm-chan.org/docs/fat.html">The basics of FAT file system [ja]</a></li>
<li><a href="http://elm-chan.org/docs/mmc/mmc_e.html">How to Use MMC/SDC</a></li> <li><a href="http://elm-chan.org/docs/mmc/mmc_e.html">How to Use MMC/SDC</a></li>
<li><a href="img/rwtest.png">Benchmark 1</a> (ATmega64/9.2MHz with MMC via SPI, HDD/CFC via GPIO)</li> <li><a href="img/rwtest.png">Benchmark 1</a> (ATmega64/9.2MHz with MMC via SPI, HDD/CFC via GPIO)</li>
<li><a href="img/rwtest2.png">Benchmark 2</a> (LPC2368/72MHz with MMC via MCI)</li> <li><a href="img/rwtest2.png">Benchmark 2</a> (LPC2368/72MHz with MMC via MCI)</li>
<li><a href="http://members.jcom.home.ne.jp/felm/fd.mp4">Demo movie of an application</a> (this project is in ffsample.zip/lpc23xx)</li>
</ul> </ul>
</div> </div>

View File

@ -19,13 +19,13 @@
<div class="abst"> <div class="abst">
<img src="img/layers.png" class="rset" width="245" height="255" alt="layer"> <img src="img/layers.png" class="rset" width="245" height="255" alt="layer">
<p>FatFsは小規模な組み込みシステム向けの汎用FATファイルシステム モジュールです。ANSI C(C89)準拠でハードウェア アーキテクチャには依存しないので、必要なワーク エリアが確保できれば、8051, PIC, AVR, SH, Z80, 68k, H8, ARMなど安価なマイコンでも使用可能です。このほか、FatFsを極小マイコン向けにシュリンクした<a href="http://elm-chan.org/fsw/ff/00index_p.html">ぷちFatFs</a>もあります。</p> <p>FatFsは小規模な組み込みシステム向けの汎用FATファイルシステム モジュールです。ANSI C(C89)準拠でハードウェア アーキテクチャには依存しないので、必要なワーク エリアが確保できれば、8051, PIC, AVR, SH, Z80, 68k, H8, ARMなど安価なマイコンでも使用可能です。このほか、FatFsを極小マイコン向けにシュリンクした<a href="http://elm-chan.org/fsw/ff/00index_p.html">ぷちFatFs</a>もあります。</p>
<h4>FatFsモジュールの特徴</h4> <h4>FatFsモジュールの特徴</h4>
<ul> <ul>
<li>Windows互換 FATファイル システム</li> <li>Windows互換 FATファイル システム</li>
<li>プラットフォーム非依存</li> <li>プラットフォーム非依存</li>
<li>コンパクトなコードとRAM使用量</li> <li>コンパクトなコードとRAM使用量</li>
<li>多くの構成オプション: <li>多くの<a href="ja/config.html">構成オプション</a>:
<ul> <ul>
<li>複数のボリューム(物理ドライブ・区画)</li> <li>複数のボリューム(物理ドライブ・区画)</li>
<li>DBCSを含む複数のANSI/OEMコード ページの選択</li> <li>DBCSを含む複数のANSI/OEMコード ページの選択</li>
@ -41,49 +41,68 @@
<div class="para"> <div class="para">
<h3>上位レイヤ インターフェース</h3> <h3>上位レイヤ インターフェース</h3>
<p>FatFsモジュールは、アプリケーション レイヤに対し、次のファイル操作関数(API)を提供します。つまり、このリストはFatFsにできることをシンプルに示しています。</p> <img src="img/layers1.png" class="rset" width="245" height="220" alt="layer">
<ul> <ul>
<li><a href="ja/mount.html">f_mount</a> - ワークエリアの登録・抹消</li> <li>ファイル アクセス
<li><a href="ja/open.html">f_open</a> - ファイルのオープン・作成</li> <ul>
<li><a href="ja/close.html">f_close</a> - ファイルのクローズ</li> <li><a href="ja/open.html">f_open</a> - ファイルのオープン・作成</li>
<li><a href="ja/read.html">f_read</a> - ファイルの読み出し</li> <li><a href="ja/close.html">f_close</a> - ファイルのクローズ</li>
<li><a href="ja/write.html">f_write</a> - ファイルの書き込み</li> <li><a href="ja/read.html">f_read</a> - データの読み出し</li>
<li><a href="ja/lseek.html">f_lseek</a> - リード/ライト ポインタの移動, ファイルの拡張</li> <li><a href="ja/write.html">f_write</a> - データの書き込み</li>
<li><a href="ja/truncate.html">f_truncate</a> - ファイル サイズの切り詰め</li> <li><a href="ja/lseek.html">f_lseek</a> - リード/ライト ポインタの移動, サイズの拡張</li>
<li><a href="ja/sync.html">f_sync</a> - キャッシュされたデータのフラッシュ</li> <li><a href="ja/truncate.html">f_truncate</a> - サイズの切り詰め</li>
<li><a href="ja/forward.html">f_forward</a> - ファイル データをストリーム関数に転送</li> <li><a href="ja/sync.html">f_sync</a> - キャッシュされたデータのフラッシュ</li>
<li><a href="ja/stat.html">f_stat</a> - ファイル/サブ ディレクトリの存在チェックと情報の取得</li> <li><a href="ja/forward.html">f_forward</a> - データをストリーム関数に転送</li>
<li><a href="ja/opendir.html">f_opendir</a> - ディレクトリのオープン</li> <li><a href="ja/gets.html">f_gets</a> - 文字列の読み出し</li>
<li><a href="ja/closedir.html">f_closedir</a> - ディレクトリのクローズ</li> <li><a href="ja/putc.html">f_putc</a> - 文字の書き込み</li>
<li><a href="ja/readdir.html">f_readdir</a> - ディレクトリの読み出し</li> <li><a href="ja/puts.html">f_puts</a> - 文字列の書き込み</li>
<li><a href="ja/mkdir.html">f_mkdir</a> - サブ ディレクトリの作成</li> <li><a href="ja/printf.html">f_printf</a> - 書式化文字列の書き込み</li>
<li><a href="ja/unlink.html">f_unlink</a> - ファイル/サブ ディレクトリの削除</li> <li><a href="ja/tell.html">f_tell</a> - リード/ライト ポインタの取得</li>
<li><a href="ja/chmod.html">f_chmod</a> - ファイル/サブ ディレクトリの属性の変更</li> <li><a href="ja/eof.html">f_eof</a> - 終端の有無の取得</li>
<li><a href="ja/utime.html">f_utime</a> - ファイル/サブ ディレクトリのタイムスタンプの変更</li> <li><a href="ja/size.html">f_size</a> - サイズの取得</li>
<li><a href="ja/rename.html">f_rename</a> - ファイル/サブ ディレクトリの名前の変更・移動</li> <li><a href="ja/error.html">f_error</a> - エラーの有無の取得</li>
<li><a href="ja/chdir.html">f_chdir</a> - カレント ディレクトリの変更</li> </ul>
<li><a href="ja/chdrive.html">f_chdrive</a> - カレント ドライブの変更</li> </li>
<li><a href="ja/getcwd.html">f_getcwd</a> - カレント ディレクトリの取得</li> <li>ディレクトリ アクセス
<li><a href="ja/getfree.html">f_getfree</a> - ボリューム空き領域の取得</li> <ul>
<li><a href="ja/getlabel.html">f_getlabel</a> - ボリューム ラベルの取得</li> <li><a href="ja/opendir.html">f_opendir</a> - ディレクトリのオープン</li>
<li><a href="ja/setlabel.html">f_setlabel</a> - ボリューム ラベルの設定</li> <li><a href="ja/closedir.html">f_closedir</a> - ディレクトリのクローズ</li>
<li><a href="ja/mkfs.html">f_mkfs</a> - 論理ドライブのフォーマット</li> <li><a href="ja/readdir.html">f_readdir</a> - 項目の読み出し</li>
<li><a href="ja/fdisk.html">f_fdisk</a> - 物理ドライブの分割</li> <li><a href="ja/findfirst.html">f_findfirst</a> - ディレクトリのオープンと最初の検索項目の読み出し</li>
<li><a href="ja/gets.html">f_gets</a> - 文字列の読み出し</li> <li><a href="ja/findnext.html">f_findnext</a> - 次の検索項目の読み出し</li>
<li><a href="ja/putc.html">f_putc</a> - 文字の書き込み</li> </ul>
<li><a href="ja/puts.html">f_puts</a> - 文字列の書き込み</li> </li>
<li><a href="ja/printf.html">f_printf</a> - 書式化文字列の書き込み</li> <li>ファイル/ディレクトリ管理
<li><a href="ja/tell.html">f_tell</a> - 現在のリード/ライト ポインタの取得</li> <ul>
<li><a href="ja/eof.html">f_eof</a> - ファイル終端の有無の取得</li> <li><a href="ja/stat.html">f_stat</a> - ファイル/サブ ディレクトリの存在チェックと情報の取得</li>
<li><a href="ja/size.html">f_size</a> - ファイル サイズの取得</li> <li><a href="ja/unlink.html">f_unlink</a> - ファイル/サブ ディレクトリの削除</li>
<li><a href="ja/error.html">f_error</a> - ファイルのエラーの有無の取得</li> <li><a href="ja/rename.html">f_rename</a> - ファイル/サブ ディレクトリの名前の変更・移動</li>
<li><a href="ja/chmod.html">f_chmod</a> - ファイル/サブ ディレクトリの属性の変更</li>
<li><a href="ja/utime.html">f_utime</a> - ファイル/サブ ディレクトリのタイムスタンプの変更</li>
<li><a href="ja/mkdir.html">f_mkdir</a> - サブ ディレクトリの作成</li>
<li><a href="ja/chdir.html">f_chdir</a> - カレント ディレクトリの変更</li>
<li><a href="ja/chdrive.html">f_chdrive</a> - カレント ドライブの変更</li>
<li><a href="ja/getcwd.html">f_getcwd</a> - カレント ディレクトリの取得</li>
</ul>
</li>
<li>ボリューム管理
<ul>
<li><a href="ja/mount.html">f_mount</a> - ボリューム ワーク エリアの登録・抹消</li>
<li><a href="ja/mkfs.html">f_mkfs</a> - 論理ドライブ上にFATボリュームを作成</li>
<li><a href="ja/fdisk.html">f_fdisk</a> - 物理ドライブ上に複数の論理ドライブを作成</li>
<li><a href="ja/getfree.html">f_getfree</a> - ボリュームのサイズと空きサイズの取得</li>
<li><a href="ja/getlabel.html">f_getlabel</a> - ボリューム ラベルの取得</li>
<li><a href="ja/setlabel.html">f_setlabel</a> - ボリューム ラベルの設定</li>
</ul>
</li>
</ul> </ul>
</div> </div>
<div class="para"> <div class="para">
<h3>下位レイヤ インターフェース</h3> <h3>下位レイヤ インターフェース</h3>
<p>FatFsモジュールは、単なるファイル システム レイヤなので、ストレージ デバイス制御レイヤは含まれません。使用するプラットフォームやストレージ デバイスに対応した制御関数は、ユーザによって提供される必要があります。FatFsモジュールは、下位レイヤに対し標準的には次のインターフェースを要求します。拡張機能、たとえばOS関連機能を有効にしたときは、加えてプロセス/メモリ操作関数なども必要になります。サンプル プロジェクトに下位レイヤの実装例を示します。</p> <img src="img/layers2.png" class="rset" width="245" height="220" alt="layer">
<p>FatFsモジュールは、単なるファイル システム レイヤなので、ストレージ デバイス制御レイヤは含まれません。使用するプラットフォームやストレージ デバイスに対応した制御関数は、インプリメンタによって提供される必要があります。FatFsモジュールは、下位レイヤに対し標準的には次のインターフェースを要求します。一部の拡張機能、たとえばOS関連機能を有効にしたときは、加えてプロセス/メモリ操作関数なども必要になります。サンプル プロジェクトに下位レイヤの実装例を示します。</p>
<ul> <ul>
<li><a href="ja/dstat.html">disk_status</a> - デバイスの状態取得</li> <li><a href="ja/dstat.html">disk_status</a> - デバイスの状態取得</li>
<li><a href="ja/dinit.html">disk_initialize</a> - デバイスの初期化</li> <li><a href="ja/dinit.html">disk_initialize</a> - デバイスの初期化</li>
@ -99,15 +118,17 @@
<h3>資料</h3> <h3>資料</h3>
<p>FatFsモジュールはフリー ソフトウェアとして教育・研究・開発用に公開しています。どのような利用目的(個人利用から商用まで)でも使用・改変・配布について一切の制限はありませんが、全て利用者の責任の下での利用とします。詳しくはアプリケーション ノートを参照してください。</p> <p>FatFsモジュールはフリー ソフトウェアとして教育・研究・開発用に公開しています。どのような利用目的(個人利用から商用まで)でも使用・改変・配布について一切の制限はありませんが、全て利用者の責任の下での利用とします。詳しくはアプリケーション ノートを参照してください。</p>
<ul> <ul>
<li><a href="http://elm-chan.org/fsw/ff/00index_j.html"><em>FatFsホームページ</em></a></li> <li>最初に読め: <a href="ja/appnote.html">FatFsモジュール アプリケーション ノート</a> <span class="mfd">2015. 3. 18</span></li>
<li><a href="http://elm-chan.org/fsw/ff/bd/"><em>FatFsユーザ フォーラム</em></a></li> <li>ダウンロード: <a href="ff11.zip">FatFs R0.11</a> | <a href="updates.txt">変更点</a> | <a href="patches.html">パッチ</a> <span class="mfd">2015. 3. 9</span></li>
<li>最初に読め: <a href="ja/appnote.html">FatFsモジュール アプリケーション ノート</a></li> <li>ダウンロード: <a href="ffsample.zip">サンプル プロジェクト</a> <span class="mfd">2015. 2. 9</span></li>
<li>ダウンロード: <a href="archives.html">旧バージョン</a></li>
<li>コミュニティ: <a href="http://elm-chan.org/fsw/ff/bd/">FatFsユーザ フォーラム</a></li>
<li><a href="http://stm32f4-discovery.com/2014/07/library-21-read-sd-card-fatfs-stm32f4xx-devices/">Read SD card with FatFs on STM32F4xx devices by Tilen Majerle</a>↗ (Quick and easy implementation for STM32F4-Discovery)</li> <li><a href="http://stm32f4-discovery.com/2014/07/library-21-read-sd-card-fatfs-stm32f4xx-devices/">Read SD card with FatFs on STM32F4xx devices by Tilen Majerle</a>↗ (Quick and easy implementation for STM32F4-Discovery)</li>
<li><a href="http://nemuisan.blog.bai.ne.jp/">ねむいさんのぶろぐ</a>↗ (Well written implementations for STM32F/SDIO and LPC2300/MCI)</li> <li><a href="http://nemuisan.blog.bai.ne.jp/">ねむいさんのぶろぐ</a>↗ (Well written implementations for STM32F/SDIO and LPC2300/MCI)</li>
<li><a href="http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/arm_memcards/index.html">ARM-Projects by Martin THOMAS</a>↗ (Examples for LPC2000, AT91SAM and STM32)</li> <li><a href="http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/arm_memcards/index.html">ARM-Projects by Martin THOMAS</a>↗ (Examples for LPC2000, AT91SAM and STM32)</li>
<li><a href="http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx">FATファイルシステム仕様 by Microsoft</a>↗ (The reference document on FAT file system)</li> <li><a href="http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx">FATファイルシステム仕様 by Microsoft</a>↗ (The reference document on FAT file system)</li>
<li><a href="http://elm-chan.org/docs/fat.html">FATファイルシステム概要</a> (↑を読むためのガイド)</li> <li><a href="http://elm-chan.org/docs/fat.html">FATファイルシステム概要</a> (↑を読むためのガイド)</li>
<li><a href="http://elm-chan.org/docs/mmc/mmc.html">MMCの使いかた</a></li> <li><a href="http://elm-chan.org/docs/mmc/mmc.html">MMCの使いかた</a></li>
<li><a href="img/rwtest.png">パフォーマンス テスト1</a> (ATmega64/9.2MHz with MMC via SPI, HDD/CFC via GPIO)</li> <li><a href="img/rwtest.png">パフォーマンス テスト1</a> (ATmega64/9.2MHz with MMC via SPI, HDD/CFC via GPIO)</li>
<li><a href="img/rwtest2.png">パフォーマンス テスト2</a> (LPC2368/72MHz with MMC via MCI)</li> <li><a href="img/rwtest2.png">パフォーマンス テスト2</a> (LPC2368/72MHz with MMC via MCI)</li>
</ul> </ul>

View File

@ -19,6 +19,7 @@ tt {margin: 0 0.2em; font-size: 0.85em; font-family: "Consolas", "Courier New",
tt.arg {font-style: italic;} tt.arg {font-style: italic;}
ol {margin: 0.5em 2.5em;} ol {margin: 0.5em 2.5em;}
ul {margin: 0.5em 2em;} ul {margin: 0.5em 2em;}
ul ul {margin: 0 2em 0.5em 1em;}
dl {margin: 0.5em 1em;} dl {margin: 0.5em 1em;}
dd {margin: 0 2em;} dd {margin: 0 2em;}
dt {font-size: 0.85em; font-family: "Consolas", "Courier New", monospace;} dt {font-size: 0.85em; font-family: "Consolas", "Courier New", monospace;}
@ -43,13 +44,14 @@ a.imglnk img {border: 1px solid;}
.cal {text-align: center; } .cal {text-align: center; }
h1 {line-height: 1em; font-size: 2em; font-family: sans-serif; padding: 0.3em 0 0.3em;} h1 {line-height: 1em; font-size: 2em; font-family: sans-serif; padding: 0.3em 0 0.3em;}
p.hdd {float: right; text-align: right; margin-top: 0.5em;}
hr.hds {clear: both; margin-bottom: 1em;}
h2 {font-size: 2em; font-family: sans-serif; background-color: #d8d8FF; padding: 0.5em 0.5em; margin: 0 0 0.5em;} h2 {font-size: 2em; font-family: sans-serif; background-color: #d8d8FF; padding: 0.5em 0.5em; margin: 0 0 0.5em;}
h3 {font-size: 1.5em; font-family: sans-serif; margin: 1.5em 0 0.5em;} h3 {font-size: 1.5em; font-family: sans-serif; margin: 1.5em 0 0.5em;}
h4 {font-size: 1.2em; font-family: sans-serif; margin: 1em 0 0.2em;} div.doc h3 {border-color: #b0d8d8; border-style: solid; border-width: 0px 0px 4px 12px; padding: 4px; margin-top: 3em;}
h5 {font-size: 1em; font-family: sans-serif; margin: 0.5em 0 0em;} h4 {font-size: 1.2em; font-family: sans-serif; margin: 2em 0 0.2em;}
h5 {font-size: 1em; font-family: sans-serif; margin: 1em 0 0em;}
p.hdd {float: right; text-align: right; margin-top: 0.5em;}
hr.hds {clear: both; margin-bottom: 1em;}
kbd {letter-spacing: 0;}
small {font-size: 80%;} small {font-size: 80%;}
.indent {margin-left: 2em;} .indent {margin-left: 2em;}

Some files were not shown because too many files have changed in this diff Show More