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

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

View file

@ -25,8 +25,7 @@
#include "string_format.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "baseband_api.hpp"
#include <algorithm>
@ -42,26 +41,6 @@ static std::string latlon_abs_normalized(const int32_t normalized, const char su
return to_string_dec_uint(degrees) + "." + to_string_dec_uint(fraction, 6, '0') + suffix;
}
static std::string latitude(const Latitude value) {
if( value.is_not_available() ) {
return "not available";
} else if( value.is_valid() ) {
return latlon_abs_normalized(value.normalized(), "SN");
} else {
return "invalid";
}
}
static std::string longitude(const Longitude value) {
if( value.is_not_available() ) {
return "not available";
} else if( value.is_valid() ) {
return latlon_abs_normalized(value.normalized(), "WE");
} else {
return "invalid";
}
}
static std::string latlon(const Latitude latitude, const Longitude longitude) {
if( latitude.is_valid() && longitude.is_valid() ) {
return latlon_abs_normalized(latitude.normalized(), "SN") + " " + latlon_abs_normalized(longitude.normalized(), "WE");
@ -78,17 +57,6 @@ static std::string mmsi(
return to_string_dec_uint(mmsi, 9);
}
static std::string datetime(
const ais::DateTime& datetime
) {
return to_string_dec_uint(datetime.year, 4, '0') + "/" +
to_string_dec_uint(datetime.month, 2, '0') + "/" +
to_string_dec_uint(datetime.day, 2, '0') + " " +
to_string_dec_uint(datetime.hour, 2, '0') + ":" +
to_string_dec_uint(datetime.minute, 2, '0') + ":" +
to_string_dec_uint(datetime.second, 2, '0');
}
static std::string navigational_status(const unsigned int value) {
switch(value) {
case 0: return "under way w/engine";
@ -160,6 +128,12 @@ static std::string true_heading(const TrueHeading value) {
} /* namespace format */
} /* namespace ais */
AISLogger::AISLogger(
const std::string& file_path
) : log_file { file_path }
{
}
void AISLogger::on_packet(const ais::Packet& packet) {
// TODO: Unstuff here, not in baseband!
if( log_file.is_ready() ) {
@ -340,22 +314,28 @@ AISAppView::AISAppView(NavigationView&) {
}
}
);
target_frequency_ = initial_target_frequency;
radio::enable({
tuning_frequency(),
sampling_rate,
baseband_bandwidth,
rf::Direction::Receive,
false, 32, 32,
1,
});
baseband::start({
.mode = 3,
.sampling_rate = sampling_rate,
.decimation_factor = 1,
});
options_channel.on_change = [this](size_t, OptionsField::value_t v) {
this->on_frequency_changed(v);
};
options_channel.set_by_value(162025000);
receiver_model.set_baseband_configuration({
.mode = 3,
.sampling_rate = 2457600,
.decimation_factor = 1,
});
receiver_model.set_baseband_bandwidth(1750000);
receiver_model.set_rf_amp(false);
receiver_model.set_lna(32);
receiver_model.set_vga(32);
receiver_model.enable();
options_channel.set_by_value(target_frequency());
recent_entries_view.on_select = [this](const AISRecentEntry& entry) {
this->on_show_detail(entry);
@ -363,10 +343,14 @@ AISAppView::AISAppView(NavigationView&) {
recent_entry_detail_view.on_close = [this]() {
this->on_show_list();
};
logger = std::make_unique<AISLogger>("ais.txt");
}
AISAppView::~AISAppView() {
receiver_model.disable();
baseband::stop();
radio::disable();
EventDispatcher::message_map().unregister_handler(Message::ID::AISPacket);
}
@ -382,7 +366,10 @@ void AISAppView::set_parent_rect(const Rect new_parent_rect) {
}
void AISAppView::on_packet(const ais::Packet& packet) {
logger.on_packet(packet);
if( logger ) {
logger->on_packet(packet);
}
const auto updated_entry = recent.on_packet(packet.source_id(), packet);
recent_entries_view.set_dirty();
@ -405,8 +392,21 @@ void AISAppView::on_show_detail(const AISRecentEntry& entry) {
recent_entry_detail_view.focus();
}
void AISAppView::on_frequency_changed(const uint32_t new_frequency) {
receiver_model.set_tuning_frequency(new_frequency);
void AISAppView::on_frequency_changed(const uint32_t new_target_frequency) {
set_target_frequency(new_target_frequency);
}
void AISAppView::set_target_frequency(const uint32_t new_value) {
target_frequency_ = new_value;
radio::set_tuning_frequency(tuning_frequency());
}
uint32_t AISAppView::target_frequency() const {
return target_frequency_;
}
uint32_t AISAppView::tuning_frequency() const {
return target_frequency() - (sampling_rate / 4);
}
} /* namespace ui */

View file

@ -90,10 +90,12 @@ using AISRecentEntries = RecentEntries<ais::Packet, AISRecentEntry>;
class AISLogger {
public:
AISLogger(const std::string& file_path);
void on_packet(const ais::Packet& packet);
private:
LogFile log_file { "ais.txt" };
LogFile log_file;
};
namespace ui {
@ -145,8 +147,12 @@ public:
std::string title() const override { return "AIS"; };
private:
static constexpr uint32_t initial_target_frequency = 162025000;
static constexpr uint32_t sampling_rate = 2457600;
static constexpr uint32_t baseband_bandwidth = 1750000;
AISRecentEntries recent;
AISLogger logger;
std::unique_ptr<AISLogger> logger;
AISRecentEntriesView recent_entries_view { recent };
AISRecentEntryDetailView recent_entry_detail_view;
@ -167,11 +173,18 @@ private:
}
};
uint32_t target_frequency_ = initial_target_frequency;
void on_packet(const ais::Packet& packet);
void on_show_list();
void on_show_detail(const AISRecentEntry& entry);
void on_frequency_changed(const uint32_t new_frequency);
void on_frequency_changed(const uint32_t new_target_frequency);
uint32_t target_frequency() const;
void set_target_frequency(const uint32_t new_value);
uint32_t tuning_frequency() const;
};
} /* namespace ui */

View file

@ -22,9 +22,12 @@
#include "analog_audio_app.hpp"
#include "portapack.hpp"
#include "portapack_shared_memory.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
#include "audio.hpp"
#include "file.hpp"
#include "utility.hpp"
namespace ui {
@ -115,6 +118,9 @@ AnalogAudioView::AnalogAudioView(
field_vga.on_change = [this](int32_t v_db) {
this->on_vga_changed(v_db);
};
field_vga.on_show_options = [this]() {
this->on_show_options_rf_gain();
};
const auto modulation = receiver_model.modulation();
options_modulation.set_by_value(modulation);
@ -125,18 +131,20 @@ AnalogAudioView::AnalogAudioView(
this->on_show_options_modulation();
};
field_volume.set_value((receiver_model.headphone_volume() - wolfson::wm8731::headphone_gain_range.max).decibel() + 99);
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
field_volume.on_change = [this](int32_t v) {
this->on_headphone_volume_changed(v);
};
audio::output::start();
update_modulation(static_cast<ReceiverModel::Mode>(modulation));
}
AnalogAudioView::~AnalogAudioView() {
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
// both?
audio_codec.headphone_mute();
audio::output::stop();
receiver_model.disable();
}
@ -212,16 +220,13 @@ void AnalogAudioView::on_show_options_frequency() {
field_frequency.set_style(&style_options_group);
auto widget = std::make_unique<FrequencyOptionsView>(
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
&style_options_group
);
auto widget = std::make_unique<FrequencyOptionsView>(options_view_rect, &style_options_group);
widget->set_step(receiver_model.frequency_step());
widget->on_change_step = [this](rf::Frequency f) {
this->on_frequency_step_changed(f);
};
widget->set_reference_ppm_correction(receiver_model.reference_ppm_correction());
widget->set_reference_ppm_correction(persistent_memory::correction_ppb() / 1000);
widget->on_change_reference_ppm_correction = [this](int32_t v) {
this->on_reference_ppm_correction_changed(v);
};
@ -235,10 +240,7 @@ void AnalogAudioView::on_show_options_rf_gain() {
field_lna.set_style(&style_options_group);
auto widget = std::make_unique<RadioGainOptionsView>(
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
&style_options_group
);
auto widget = std::make_unique<RadioGainOptionsView>(options_view_rect, &style_options_group);
widget->set_rf_amp(receiver_model.rf_amp());
widget->on_change_rf_amp = [this](bool enable) {
@ -255,18 +257,12 @@ void AnalogAudioView::on_show_options_modulation() {
const auto modulation = static_cast<ReceiverModel::Mode>(receiver_model.modulation());
if( modulation == ReceiverModel::Mode::AMAudio ) {
options_modulation.set_style(&style_options_group);
auto widget = std::make_unique<AMOptionsView>(
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
&style_options_group
);
auto widget = std::make_unique<AMOptionsView>(options_view_rect, &style_options_group);
set_options_widget(std::move(widget));
}
if( modulation == ReceiverModel::Mode::NarrowbandFMAudio ) {
options_modulation.set_style(&style_options_group);
auto widget = std::make_unique<NBFMOptionsView>(
Rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
&style_options_group
);
auto widget = std::make_unique<NBFMOptionsView>(options_view_rect, &style_options_group);
set_options_widget(std::move(widget));
}
}
@ -277,15 +273,18 @@ void AnalogAudioView::on_frequency_step_changed(rf::Frequency f) {
}
void AnalogAudioView::on_reference_ppm_correction_changed(int32_t v) {
receiver_model.set_reference_ppm_correction(v);
persistent_memory::set_correction_ppb(v * 1000);
}
void AnalogAudioView::on_headphone_volume_changed(int32_t v) {
const auto new_volume = volume_t::decibel(v - 99) + wolfson::wm8731::headphone_gain_range.max;
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
}
void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
audio::output::mute();
audio_thread.reset();
const auto is_wideband_spectrum_mode = (modulation == ReceiverModel::Mode::SpectrumAnalysis);
receiver_model.set_baseband_configuration({
.mode = toUType(modulation),
@ -294,6 +293,14 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
});
receiver_model.set_baseband_bandwidth(is_wideband_spectrum_mode ? 12000000 : 1750000);
receiver_model.enable();
if( !is_wideband_spectrum_mode ) {
const auto filename = next_filename_matching_pattern("AUD_????.S16");
if( !filename.empty() ) {
audio_thread = std::make_unique<AudioThread>(filename);
}
audio::output::unmute();
}
}
} /* namespace ui */

View file

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

View file

@ -0,0 +1,174 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "audio.hpp"
#include "portapack.hpp"
using portapack::i2c0;
using portapack::clock_manager;
#include "wm8731.hpp"
using wolfson::wm8731::WM8731;
#include "i2s.hpp"
using namespace lpc43xx;
namespace audio {
namespace {
constexpr i2s::ConfigTX i2s0_config_tx {
.dao = i2s::DAO {
.wordwidth = i2s::WordWidth::Bits16,
.mono = 0,
.stop = 1,
.reset = 0,
.ws_sel = 0,
.ws_halfperiod = 0x0f,
.mute = 1,
},
.txrate = i2s::MCLKRate {
.x_divider = 0,
.y_divider = 0,
},
.txbitrate = i2s::BitRate {
.bitrate = 7,
},
.txmode = i2s::Mode {
.clksel = i2s::ClockSelect::BaseAudioClkOrExternalMCLK,
.four_pin = 0,
.mclk_out_en = 1,
},
};
constexpr i2s::ConfigRX i2s0_config_rx {
.dai = i2s::DAI {
.wordwidth = i2s::WordWidth::Bits16,
.mono = 0,
.stop = 1,
.reset = 0,
.ws_sel = 1,
.ws_halfperiod = 0x0f,
},
.rxrate = i2s::MCLKRate {
.x_divider = 0,
.y_divider = 0,
},
.rxbitrate = i2s::BitRate {
.bitrate = 7,
},
.rxmode = i2s::Mode {
.clksel = i2s::ClockSelect::BaseAudioClkOrExternalMCLK,
.four_pin = 1,
.mclk_out_en = 0,
},
};
constexpr i2s::ConfigDMA i2s0_config_dma {
.dma1 = i2s::DMA {
.rx_enable = 1,
.tx_enable = 0,
.rx_depth = 4,
.tx_depth = 0,
},
.dma2 = i2s::DMA {
.rx_enable = 0,
.tx_enable = 1,
.rx_depth = 0,
.tx_depth = 4,
},
};
constexpr uint8_t wm8731_i2c_address = 0x1a;
WM8731 audio_codec { i2c0, wm8731_i2c_address };
} /* namespace */
namespace output {
void start() {
i2s::i2s0::tx_start();
unmute();
}
void stop() {
mute();
i2s::i2s0::tx_stop();
}
void mute() {
i2s::i2s0::tx_mute();
audio_codec.headphone_mute();
}
void unmute() {
i2s::i2s0::tx_unmute();
}
} /* namespace output */
namespace headphone {
volume_range_t volume_range() {
return wolfson::wm8731::headphone_gain_range;
}
void set_volume(const volume_t volume) {
audio_codec.set_headphone_volume(volume);
}
} /* namespace headphone */
namespace debug {
int reg_count() {
return wolfson::wm8731::reg_count;
}
uint16_t reg_read(const int register_number) {
return audio_codec.read(register_number);
}
} /* namespace debug */
void init() {
clock_manager.start_audio_pll();
audio_codec.init();
i2s::i2s0::configure(
i2s0_config_tx,
i2s0_config_rx,
i2s0_config_dma
);
}
void shutdown() {
audio_codec.reset();
output::stop();
}
void set_rate(const Rate rate) {
clock_manager.set_base_audio_clock_divider(toUType(rate));
}
} /* namespace audio */

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

@ -0,0 +1,24 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "audio_thread.hpp"
Thread* AudioThread::thread = nullptr;

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.
*/
#ifndef __M4_STARTUP_H__
#define __M4_STARTUP_H__
#ifndef __CORE_CONTROL_H__
#define __CORE_CONTROL_H__
#include <cstddef>
@ -34,4 +34,6 @@ void m4_request_shutdown();
void m4_switch(const char * hash);
int m4_load_image(void);
#endif/*__M4_STARTUP_H__*/
void m0_halt();
#endif/*__CORE_CONTROL_H__*/

View file

@ -23,8 +23,7 @@
#include "event_m0.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "baseband_api.hpp"
#include "manchester.hpp"
@ -52,10 +51,20 @@ std::string consumption(Consumption value) {
return to_string_dec_uint(value, 10);
}
std::string commodity_type(CommodityType value) {
return to_string_dec_uint(value, 2);
}
} /* namespace format */
} /* namespace ert */
ERTLogger::ERTLogger(
const std::string& file_path
) : log_file { file_path }
{
}
void ERTLogger::on_packet(const ert::Packet& packet) {
if( log_file.is_ready() ) {
const auto formatted = packet.symbols_formatted();
@ -63,6 +72,8 @@ void ERTLogger::on_packet(const ert::Packet& packet) {
}
}
const ERTRecentEntry::Key ERTRecentEntry::invalid_key { };
void ERTRecentEntry::update(const ert::Packet& packet) {
received_count++;
@ -71,8 +82,9 @@ void ERTRecentEntry::update(const ert::Packet& packet) {
namespace ui {
static const std::array<std::pair<std::string, size_t>, 3> ert_columns { {
static const std::array<std::pair<std::string, size_t>, 4> ert_columns { {
{ "ID", 10 },
{ "Tp", 2 },
{ "Consumpt", 10 },
{ "Cnt", 3 },
} };
@ -106,7 +118,7 @@ void RecentEntriesView<ERTRecentEntries>::draw(
) {
const auto& draw_style = is_selected ? style.invert() : style;
std::string line = ert::format::id(entry.id) + " " + ert::format::consumption(entry.last_consumption);
std::string line = ert::format::id(entry.id) + " " + ert::format::commodity_type(entry.commodity_type) + " " + ert::format::consumption(entry.last_consumption);
if( entry.received_count > 999 ) {
line += " +++";
@ -131,21 +143,28 @@ ERTAppView::ERTAppView(NavigationView&) {
}
);
receiver_model.set_baseband_configuration({
radio::enable({
initial_target_frequency,
sampling_rate,
baseband_bandwidth,
rf::Direction::Receive,
false, 32, 32,
1,
});
baseband::start({
.mode = 6,
.sampling_rate = 4194304,
.sampling_rate = sampling_rate,
.decimation_factor = 1,
});
receiver_model.set_baseband_bandwidth(2500000);
receiver_model.set_rf_amp(false);
receiver_model.set_lna(32);
receiver_model.set_vga(32);
receiver_model.set_tuning_frequency(911600000);
receiver_model.enable();
logger = std::make_unique<ERTLogger>("ert.txt");
}
ERTAppView::~ERTAppView() {
receiver_model.disable();
baseband::stop();
radio::disable();
EventDispatcher::message_map().unregister_handler(Message::ID::ERTPacket);
}
@ -159,10 +178,12 @@ void ERTAppView::set_parent_rect(const Rect new_parent_rect) {
}
void ERTAppView::on_packet(const ert::Packet& packet) {
logger.on_packet(packet);
if( logger ) {
logger->on_packet(packet);
}
if( packet.crc_ok() ) {
recent.on_packet(packet.id(), packet);
recent.on_packet({ packet.id(), packet.commodity_type() }, packet);
recent_entries_view.set_dirty();
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -21,13 +21,24 @@
#include "file.hpp"
#include <algorithm>
File::~File() {
close();
}
bool File::open_for_append(const std::string file_path) {
bool File::open_for_writing(const std::string& file_path) {
const auto open_result = f_open(&f, file_path.c_str(), FA_WRITE | FA_OPEN_ALWAYS);
if( open_result == FR_OK ) {
return (open_result == FR_OK);
}
bool File::open_for_reading(const std::string& file_path) {
const auto open_result = f_open(&f, file_path.c_str(), FA_READ | FA_OPEN_EXISTING);
return (open_result == FR_OK);
}
bool File::open_for_append(const std::string& file_path) {
if( open_for_writing(file_path) ) {
const auto seek_result = f_lseek(&f, f_size(&f));
if( seek_result == FR_OK ) {
return true;
@ -60,7 +71,7 @@ bool File::write(const void* const data, const size_t bytes_to_write) {
return (result == FR_OK) && (bytes_written == bytes_to_write);
}
bool File::puts(const std::string string) {
bool File::puts(const std::string& string) {
const auto result = f_puts(string.c_str(), &f);
return (result >= 0);
}
@ -69,3 +80,91 @@ bool File::sync() {
const auto result = f_sync(&f);
return (result == FR_OK);
}
static std::string find_last_file_matching_pattern(const std::string& pattern) {
std::string last_match;
for(const auto& entry : std::filesystem::directory_iterator("", pattern.c_str())) {
if( std::filesystem::is_regular_file(entry.status()) ) {
const auto match = entry.path();
if( match > last_match ) {
last_match = match;
}
}
}
return last_match;
}
static std::string increment_filename_ordinal(const std::string& filename) {
std::string result { filename };
auto it = result.rbegin();
// Back up past extension.
for(; it != result.rend(); ++it) {
if( *it == '.' ) {
++it;
break;
}
}
if( it == result.rend() ) {
return { };
}
// Increment decimal number before the extension.
for(; it != result.rend(); ++it) {
const auto c = *it;
if( c < '0' ) {
return { };
} else if( c < '9' ) {
*it += 1;
break;
} else if( c == '9' ) {
*it = '0';
} else {
return { };
}
}
return result;
}
std::string next_filename_matching_pattern(const std::string& filename_pattern) {
auto filename = find_last_file_matching_pattern(filename_pattern);
if( filename.empty() ) {
filename = filename_pattern;
std::replace(std::begin(filename), std::end(filename), '?', '0');
} else {
filename = increment_filename_ordinal(filename);
}
return filename;
}
namespace std {
namespace filesystem {
directory_iterator::directory_iterator(
const char* path,
const char* wild
) {
impl = std::make_shared<Impl>();
const auto result = f_findfirst(&impl->dir, &impl->filinfo, path, wild);
if( result != FR_OK ) {
impl.reset();
// TODO: Throw exception if/when I enable exceptions...
}
}
directory_iterator& directory_iterator::operator++() {
const auto result = f_findnext(&impl->dir, &impl->filinfo);
if( (result != FR_OK) || (impl->filinfo.fname[0] == 0) ) {
impl.reset();
}
return *this;
}
bool is_regular_file(const file_status s) {
return !(s & AM_DIR);
}
} /* namespace filesystem */
} /* namespace std */

View file

@ -26,12 +26,17 @@
#include <cstddef>
#include <string>
#include <array>
#include <memory>
#include <iterator>
class File {
public:
~File();
bool open_for_append(const std::string file_path);
bool open_for_writing(const std::string& file_path);
bool open_for_reading(const std::string& file_path);
bool open_for_append(const std::string& file_path);
bool close();
bool is_ready();
@ -39,14 +44,76 @@ public:
bool read(void* const data, const size_t bytes_to_read);
bool write(const void* const data, const size_t bytes_to_write);
bool puts(const std::string string);
template<size_t N>
bool write(const std::array<uint8_t, N>& data) {
return write(data.data(), N);
}
bool puts(const std::string& string);
bool sync();
private:
const std::string file_path;
FIL f;
};
std::string next_filename_matching_pattern(const std::string& filename_pattern);
namespace std {
namespace filesystem {
using file_status = BYTE;
struct directory_entry : public FILINFO {
file_status status() const {
return fattrib;
}
const std::string path() const noexcept { return fname; };
};
class directory_iterator {
struct Impl {
DIR dir;
directory_entry filinfo;
~Impl() {
f_closedir(&dir);
}
};
std::shared_ptr<Impl> impl;
friend bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs);
public:
using difference_type = std::ptrdiff_t;
using value_type = directory_entry;
using pointer = const directory_entry*;
using reference = const directory_entry&;
using iterator_category = std::input_iterator_tag;
directory_iterator() noexcept { };
directory_iterator(const char* path, const char* wild);
~directory_iterator() { }
directory_iterator& operator++();
reference operator*() const {
// TODO: Exception or assert if impl == nullptr.
return impl->filinfo;
}
};
inline const directory_iterator& begin(const directory_iterator& iter) noexcept { return iter; };
inline directory_iterator end(const directory_iterator&) noexcept { return { }; };
inline bool operator!=(const directory_iterator& lhs, const directory_iterator& rhs) { return lhs.impl != rhs.impl; };
bool is_regular_file(const file_status s);
} /* namespace filesystem */
} /* namespace std */
#endif/*__FILE_H__*/

View file

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

View file

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

View file

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

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

View file

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

View file

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

View file

@ -22,9 +22,11 @@
#include "radio.hpp"
#include "rf_path.hpp"
#include "rffc507x.hpp"
#include "max2837.hpp"
#include "max5864.hpp"
#include "baseband_cpld.hpp"
#include "portapack_shared_memory.hpp"
#include "tuning.hpp"
@ -148,6 +150,10 @@ void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum) {
second_if.set_lpf_rf_bandwidth(bandwidth_minimum);
}
void set_baseband_rate(const uint32_t rate) {
portapack::clock_manager.set_sampling_frequency(rate);
}
void set_baseband_decimation_by(const size_t n) {
baseband_cpld.set_decimation_by(n);
}
@ -165,4 +171,43 @@ void disable() {
set_rf_amp(false);
}
void enable(Configuration configuration) {
configure(configuration);
}
void configure(Configuration configuration) {
set_tuning_frequency(configuration.tuning_frequency);
set_rf_amp(configuration.rf_amp);
set_lna_gain(configuration.lna_gain);
set_vga_gain(configuration.vga_gain);
set_baseband_rate(configuration.baseband_rate);
set_baseband_decimation_by(configuration.baseband_decimation);
set_baseband_filter_bandwidth(configuration.baseband_filter_bandwidth);
set_direction(configuration.direction);
}
namespace debug {
namespace first_if {
uint32_t register_read(const size_t register_number) {
return radio::first_if.read(register_number);
}
} /* namespace first_if */
namespace second_if {
uint32_t register_read(const size_t register_number) {
return radio::second_if.read(register_number);
}
uint8_t temp_sense() {
return radio::second_if.temp_sense() & 0x1f;
}
} /* namespace second_if */
} /* namespace debug */
} /* namespace radio */

View file

@ -27,11 +27,19 @@
#include <cstdint>
#include <cstddef>
#include "rffc507x.hpp"
#include "max2837.hpp"
namespace radio {
struct Configuration {
rf::Frequency tuning_frequency;
uint32_t baseband_rate;
uint32_t baseband_filter_bandwidth;
rf::Direction direction;
bool rf_amp;
int8_t lna_gain;
int8_t vga_gain;
uint8_t baseband_decimation;
};
void init();
void set_direction(const rf::Direction new_direction);
@ -39,15 +47,33 @@ bool set_tuning_frequency(const rf::Frequency frequency);
void set_rf_amp(const bool rf_amp);
void set_lna_gain(const int_fast8_t db);
void set_vga_gain(const int_fast8_t db);
void set_sampling_frequency(const uint32_t frequency);
void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum);
void set_baseband_rate(const uint32_t rate);
void set_baseband_decimation_by(const size_t n);
void set_antenna_bias(const bool on);
void enable(Configuration configuration);
void configure(Configuration configuration);
void disable();
extern rffc507x::RFFC507x first_if;
extern max2837::MAX2837 second_if;
namespace debug {
namespace first_if {
uint32_t register_read(const size_t register_number);
} /* namespace first_if */
namespace second_if {
uint32_t register_read(const size_t register_number);
// TODO: This belongs somewhere else.
uint8_t temp_sense();
} /* namespace second_if */
} /* namespace debug */
} /* namespace radio */

View file

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

View file

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

View file

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

View file

@ -278,21 +278,4 @@ spi::reg_t RFFC507x::readback(const Readback readback) {
return read(Register::READBACK);
}
#if 0
/* Test of RFFC507x reset over temperature */
while(true) {
first_if.write(rffc507x::Register::P1_FREQ2, 0xAAAA);
first_if.reset();
const auto after_reset = first_if.read(rffc507x::Register::P1_FREQ2);
if( after_reset != 0x6276 ) {
led_usb.off();
led_tx.on();
chThdSleepMilliseconds(100);
} else {
led_usb.on();
led_tx.off();
}
}
#endif
} /* namespace rffc507x */

View file

@ -39,16 +39,12 @@ FRESULT mount() {
return f_mount(&fs, "", 0);
}
FRESULT unmount() {
return f_mount(NULL, "", 0);
}
} /* namespace */
Signal<Status> status_signal;
void poll_inserted() {
const auto card_present_now = sdc_lld_is_card_inserted(&SDCD1);
const auto card_present_now = sdcIsCardInserted(&SDCD1);
if( card_present_now != card_present ) {
card_present = card_present_now;

View file

@ -54,7 +54,7 @@ std::vector<TemperatureLogger::sample_t> TemperatureLogger::history() const {
TemperatureLogger::sample_t TemperatureLogger::read_sample() {
// MAX2837 does not return a valid temperature if in "shutdown" mode.
return radio::second_if.temp_sense() & 0x1f;
return radio::debug::second_if::temp_sense();
}
void TemperatureLogger::push_sample(const TemperatureLogger::sample_t sample) {

View file

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

View file

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

View file

@ -23,13 +23,10 @@
#include "event_m0.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "crc.hpp"
#include "utility.hpp"
namespace tpms {
@ -54,96 +51,20 @@ std::string temperature(Temperature temperature) {
} /* namespace format */
Timestamp Packet::received_at() const {
return packet_.timestamp();
}
ManchesterFormatted Packet::symbols_formatted() const {
return format_manchester(decoder_);
}
Optional<Reading> Packet::reading() const {
const auto length = crc_valid_length();
switch(length) {
case 64:
return Reading {
Reading::Type::FLM_64,
reader_.read(0, 32),
Pressure { static_cast<int>(reader_.read(32, 8)) * 4 / 3 },
Temperature { static_cast<int>(reader_.read(40, 8) & 0x7f) - 50 }
};
case 72:
return Reading {
Reading::Type::FLM_72,
reader_.read(0, 32),
Pressure { static_cast<int>(reader_.read(40, 8)) * 4 / 3 },
Temperature { static_cast<int>(reader_.read(48, 8)) - 50 }
};
case 80:
return Reading {
Reading::Type::FLM_80,
reader_.read(8, 32),
Pressure { static_cast<int>(reader_.read(48, 8)) * 4 / 3 },
Temperature { static_cast<int>(reader_.read(56, 8)) - 50 }
};
default:
return { };
}
}
size_t Packet::crc_valid_length() const {
constexpr uint32_t checksum_bytes = 0b1111111;
constexpr uint32_t crc_72_bytes = 0b111111111;
constexpr uint32_t crc_80_bytes = 0b1111111110;
std::array<uint8_t, 10> bytes;
for(size_t i=0; i<bytes.size(); i++) {
bytes[i] = reader_.read(i * 8, 8);
}
uint32_t checksum = 0;
CRC<uint8_t> crc_72 { 0x01, 0x00 };
CRC<uint8_t> crc_80 { 0x01, 0x00 };
for(size_t i=0; i<bytes.size(); i++) {
const uint32_t byte_mask = 1 << i;
const auto byte = bytes[i];
if( checksum_bytes & byte_mask ) {
checksum += byte;
}
if( crc_72_bytes & byte_mask ) {
crc_72.process_byte(byte);
}
if( crc_80_bytes & byte_mask ) {
crc_80.process_byte(byte);
}
}
if( crc_80.checksum() == 0 ) {
return 80;
} else if( crc_72.checksum() == 0 ) {
return 72;
} else if( (checksum & 0xff) == bytes[7] ) {
return 64;
} else {
return 0;
}
}
} /* namespace tpms */
void TPMSLogger::on_packet(const tpms::Packet& packet) {
TPMSLogger::TPMSLogger(
const std::string& file_path
) : log_file { file_path }
{
}
void TPMSLogger::on_packet(const tpms::Packet& packet, const uint32_t target_frequency) {
const auto hex_formatted = packet.symbols_formatted();
if( log_file.is_ready() ) {
const auto tuning_frequency = receiver_model.tuning_frequency();
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
const auto tuning_frequency_str = to_string_dec_uint(tuning_frequency, 10);
const auto tuning_frequency_str = to_string_dec_uint(target_frequency, 10);
std::string entry = tuning_frequency_str + " FSK 38.4 19.2 " + hex_formatted.data + "/" + hex_formatted.errors;
log_file.write_entry(packet.received_at(), entry);
@ -235,25 +156,32 @@ TPMSAppView::TPMSAppView(NavigationView&) {
[this](Message* const p) {
const auto message = static_cast<const TPMSPacketMessage*>(p);
const tpms::Packet packet { message->packet };
this->on_packet(packet);
this->on_packet(message->signal_type, packet);
}
);
receiver_model.set_baseband_configuration({
radio::enable({
tuning_frequency(),
sampling_rate,
baseband_bandwidth,
rf::Direction::Receive,
false, 32, 32,
1,
});
baseband::start({
.mode = 5,
.sampling_rate = 2457600,
.sampling_rate = sampling_rate,
.decimation_factor = 1,
});
receiver_model.set_baseband_bandwidth(1750000);
receiver_model.set_rf_amp(false);
receiver_model.set_lna(32);
receiver_model.set_vga(32);
receiver_model.set_tuning_frequency(315000000);
receiver_model.enable();
logger = std::make_unique<TPMSLogger>("tpms.txt");
}
TPMSAppView::~TPMSAppView() {
receiver_model.disable();
baseband::stop();
radio::disable();
EventDispatcher::message_map().unregister_handler(Message::ID::TPMSPacket);
}
@ -266,10 +194,12 @@ void TPMSAppView::set_parent_rect(const Rect new_parent_rect) {
recent_entries_view.set_parent_rect({ 0, 0, new_parent_rect.width(), new_parent_rect.height() });
}
void TPMSAppView::on_packet(const tpms::Packet& packet) {
logger.on_packet(packet);
void TPMSAppView::on_packet(const tpms::SignalType signal_type, const tpms::Packet& packet) {
if( logger ) {
logger->on_packet(packet, target_frequency());
}
const auto reading_opt = packet.reading();
const auto reading_opt = packet.reading(signal_type);
if( reading_opt.is_valid() ) {
const auto reading = reading_opt.value();
recent.on_packet({ reading.type(), reading.id() }, reading);
@ -282,4 +212,12 @@ void TPMSAppView::on_show_list() {
recent_entries_view.focus();
}
uint32_t TPMSAppView::target_frequency() const {
return initial_target_frequency;
}
uint32_t TPMSAppView::tuning_frequency() const {
return target_frequency() - (sampling_rate / 4);
}
} /* namespace ui */

View file

@ -25,127 +25,11 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "field_reader.hpp"
#include "baseband_packet.hpp"
#include "manchester.hpp"
#include "log_file.hpp"
#include "recent_entries.hpp"
#include "optional.hpp"
#include "units.hpp"
using units::Temperature;
using units::Pressure;
namespace tpms {
class TransponderID {
public:
constexpr TransponderID(
) : id_ { 0 }
{
}
constexpr TransponderID(
const uint32_t id
) : id_ { id }
{
}
constexpr uint32_t value() const {
return id_;
}
private:
uint32_t id_;
};
class Reading {
public:
enum Type {
None = 0,
FLM_64 = 1,
FLM_72 = 2,
FLM_80 = 3,
};
constexpr Reading(
) : type_ { Type::None }
{
}
constexpr Reading(
Type type,
TransponderID id
) : type_ { type },
id_ { id }
{
}
constexpr Reading(
Type type,
TransponderID id,
Optional<Pressure> pressure = { },
Optional<Temperature> temperature = { }
) : type_ { type },
id_ { id },
pressure_ { pressure },
temperature_ { temperature }
{
}
Type type() const {
return type_;
}
TransponderID id() const {
return id_;
}
Optional<Pressure> pressure() const {
return pressure_;
}
Optional<Temperature> temperature() const {
return temperature_;
}
private:
Type type_ { Type::None };
TransponderID id_ { 0 };
Optional<Pressure> pressure_ { };
Optional<Temperature> temperature_ { };
};
class Packet {
public:
constexpr Packet(
const baseband::Packet& packet
) : packet_ { packet },
decoder_ { packet_, 0 },
reader_ { decoder_ }
{
}
Timestamp received_at() const;
ManchesterFormatted symbols_formatted() const;
Optional<Reading> reading() const;
private:
using Reader = FieldReader<ManchesterDecoder, BitRemapNone>;
const baseband::Packet packet_;
const ManchesterDecoder decoder_;
const Reader reader_;
size_t crc_valid_length() const;
};
} /* namespace tpms */
#include "tpms_packet.hpp"
namespace std {
@ -186,10 +70,12 @@ using TPMSRecentEntries = RecentEntries<tpms::Reading, TPMSRecentEntry>;
class TPMSLogger {
public:
void on_packet(const tpms::Packet& packet);
TPMSLogger(const std::string& file_path);
void on_packet(const tpms::Packet& packet, const uint32_t target_frequency);
private:
LogFile log_file { "tpms.txt" };
LogFile log_file;
};
namespace ui {
@ -212,13 +98,20 @@ public:
std::string title() const override { return "TPMS"; };
private:
static constexpr uint32_t initial_target_frequency = 315000000;
static constexpr uint32_t sampling_rate = 2457600;
static constexpr uint32_t baseband_bandwidth = 1750000;
TPMSRecentEntries recent;
TPMSLogger logger;
std::unique_ptr<TPMSLogger> logger;
TPMSRecentEntriesView recent_entries_view { recent };
void on_packet(const tpms::Packet& packet);
void on_packet(const tpms::SignalType signal_type, const tpms::Packet& packet);
void on_show_list();
uint32_t target_frequency() const;
uint32_t tuning_frequency() const;
};
} /* namespace ui */

View file

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

View file

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

View file

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

View file

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

View file

@ -32,17 +32,19 @@ void MenuItemView::select() {
}
void MenuItemView::highlight() {
set_highlight(true);
set_highlighted(true);
set_dirty();
}
void MenuItemView::unhighlight() {
set_highlight(false);
set_highlighted(false);
set_dirty();
}
void MenuItemView::paint(Painter& painter) {
const auto r = screen_rect();
const auto paint_style = (flags.highlighted && parent()->has_focus()) ? style().invert() : style();
const auto paint_style = (highlighted() && parent()->has_focus()) ? style().invert() : style();
const auto font_height = paint_style.font.line_height();
@ -68,11 +70,6 @@ void MenuItemView::paint(Painter& painter) {
);
}
void MenuItemView::set_highlight(const bool value) {
flags.highlighted = value;
set_dirty();
}
/* MenuView **************************************************************/
MenuView::~MenuView() {

View file

@ -36,6 +36,10 @@ struct MenuItem {
std::string text;
ui::Color color;
std::function<void(void)> on_select;
// TODO: Prevent default-constructed MenuItems.
// I managed to construct a menu with three extra, unspecified menu items
// in the array that were default constructed...
};
class MenuItemView : public Widget {
@ -54,8 +58,6 @@ public:
private:
const MenuItem item;
void set_highlight(const bool value);
};
class MenuView : public View {
@ -63,7 +65,7 @@ public:
std::function<void(void)> on_left;
MenuView() {
flags.focusable = true;
set_focusable(true);
}
~MenuView();
@ -71,7 +73,7 @@ public:
void add_item(const MenuItem item);
template<size_t N>
void add_items(const std::array<MenuItem, N> items) {
void add_items(const std::array<MenuItem, N>& items) {
for(const auto& item : items) {
add_item(item);
}

View file

@ -23,37 +23,23 @@
#include "portapack.hpp"
#include "event_m0.hpp"
#include "receiver_model.hpp"
#include "transmitter_model.hpp"
#include "portapack_persistent_memory.hpp"
#include "splash.hpp"
#include "ui_about.hpp"
#include "ui_setup.hpp"
#include "ui_debug.hpp"
#include "ui_receiver.hpp"
#include "ui_rds.hpp"
#include "ui_lcr.hpp"
#include "ui_whistle.hpp"
#include "ui_jammer.hpp"
#include "ui_loadmodule.hpp"
#include "ui_afskrx.hpp"
#include "ui_xylos.hpp"
#include "ui_sigfrx.hpp"
#include "ui_numbers.hpp"
#include "analog_audio_app.hpp"
#include "ais_app.hpp"
#include "ert_app.hpp"
#include "tpms_app.hpp"
#include "capture_app.hpp"
#include "m4_startup.hpp"
#include "spi_image.hpp"
#include "core_control.hpp"
#include "modules.h"
using namespace portapack;
#include "file.hpp"
#include "png_writer.hpp"
namespace ui {
@ -63,6 +49,7 @@ SystemStatusView::SystemStatusView() {
add_children({ {
&button_back,
&title,
&button_camera,
&button_sleep,
&sd_card_status_view,
} });
@ -73,14 +60,19 @@ SystemStatusView::SystemStatusView() {
}
};
button_camera.on_select = [this](ImageButton&) {
this->on_camera();
};
button_sleep.on_select = [this](ImageButton&) {
DisplaySleepMessage message;
EventDispatcher::message_map().send(&message);
};
}
void SystemStatusView::set_back_visible(bool new_value) {
button_back.hidden(!new_value);
void SystemStatusView::set_back_enabled(bool new_value) {
button_back.set_text(new_value ? back_text_enabled : back_text_disabled);
button_back.set_focusable(new_value);
}
void SystemStatusView::set_title(const std::string new_value) {
@ -91,6 +83,21 @@ void SystemStatusView::set_title(const std::string new_value) {
}
}
void SystemStatusView::on_camera() {
const auto filename = next_filename_matching_pattern("SCR_????.PNG");
if( filename.empty() ) {
return;
}
PNGWriter png { filename };
for(int i=0; i<320; i++) {
std::array<ColorRGB888, 240> row;
portapack::display.read_pixels({ 0, i, 240, 1 }, row);
png.write_scanline(row);
}
}
/* Navigation ************************************************************/
bool NavigationView::is_top() const {
@ -108,10 +115,6 @@ View* NavigationView::push_view(std::unique_ptr<View> new_view) {
return p;
}
void NavigationView::push(View* v) {
push_view(std::unique_ptr<View>(v));
}
void NavigationView::pop() {
// Can't pop last item from stack.
if( view_stack.size() > 1 ) {
@ -153,25 +156,37 @@ void NavigationView::focus() {
TranspondersMenuView::TranspondersMenuView(NavigationView& nav) {
add_items<3>({ {
{ "AIS: Boats", ui::Color::white(), [&nav](){ nav.push<AISAppView>(); } },
{ "ERT: Utility Meters", ui::Color::white(), [&nav](){ nav.push<ERTAppView>(); } },
{ "TPMS: Cars", ui::Color::white(), [&nav](){ nav.push<TPMSAppView>(); } },
{ "AIS: Boats", [&nav](){ nav.push<AISAppView>(); } },
{ "ERT: Utility Meters", [&nav](){ nav.push<ERTAppView>(); } },
{ "TPMS: Cars", [&nav](){ nav.push<TPMSAppView>(); } },
} });
on_left = [&nav](){ nav.pop(); };
}
/* ReceiverMenuView ******************************************************/
ReceiverMenuView::ReceiverMenuView(NavigationView& nav) {
add_items<2>({ {
{ "Audio", ui::Color::white(), [&nav](){ nav.push<AnalogAudioView>(); } },
{ "Transponders", ui::Color::white(), [&nav](){ nav.push<TranspondersMenuView>(); } },
{ "Audio", [&nav](){ nav.push<AnalogAudioView>(); } },
{ "Transponders", [&nav](){ nav.push<TranspondersMenuView>(); } },
} });
on_left = [&nav](){ nav.pop(); };
}
/* SystemMenuView ********************************************************/
SystemMenuView::SystemMenuView(NavigationView& nav) {
add_items<10>({ {
add_items<7>({ {
{ "Receiver", [&nav](){ nav.push<ReceiverMenuView>(); } },
{ "Capture", [&nav](){ nav.push<NotImplementedView>(); } },
{ "Analyze", [&nav](){ nav.push<NotImplementedView>(); } },
{ "Setup", [&nav](){ nav.push<SetupMenuView>(); } },
{ "About", [&nav](){ nav.push<AboutView>(); } },
{ "Debug", [&nav](){ nav.push<DebugMenuView>(); } },
{ "HackRF", [&nav](){ nav.push<HackRFFirmwareView>(); } },
} });
/* add_items<10>({ {
{ "Play dead", ui::Color::red(), [&nav](){ nav.push<PlayDeadView>(false); } },
{ "Receiver", ui::Color::cyan(), [&nav](){ nav.push<LoadModuleView>(md5_baseband, new ReceiverMenuView(nav)); } },
//{ "Nordic/BTLE RX", ui::Color::cyan(), [&nav](){ nav.push(new NotImplementedView { nav }); } },
@ -190,7 +205,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
{ "About", ui::Color::white(), [&nav](){ nav.push<AboutView>(); } },
{ "Debug", ui::Color::white(), [&nav](){ nav.push<DebugMenuView>(); } },
{ "HackRF", ui::Color::white(), [&nav](){ nav.push<HackRFFirmwareView>(); } },
} });
} });*/
}
/* SystemView ************************************************************/
@ -207,7 +222,7 @@ SystemView::SystemView(
) : View { parent_rect },
context_(context)
{
style_ = &style_default;
set_style(&style_default);
constexpr ui::Dim status_view_height = 16;
@ -226,7 +241,7 @@ SystemView::SystemView(
{ parent_rect.width(), static_cast<ui::Dim>(parent_rect.height() - status_view_height) }
});
navigation_view.on_view_changed = [this](const View& new_view) {
this->status_view.set_back_visible(!this->navigation_view.is_top());
this->status_view.set_back_enabled(!this->navigation_view.is_top());
this->status_view.set_title(new_view.title());
};

View file

@ -60,21 +60,46 @@ static constexpr Bitmap bitmap_sleep {
{ 16, 16 }, bitmap_sleep_data
};
static constexpr uint8_t bitmap_camera_data[] = {
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0xcc, 0x03,
0xe8, 0x07,
0xfc, 0x3f,
0x3c, 0x3c,
0x9c, 0x39,
0xdc, 0x3b,
0xdc, 0x3b,
0x9c, 0x39,
0x3c, 0x3c,
0xfc, 0x3f,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
};
static constexpr Bitmap bitmap_camera {
{ 16, 16 }, bitmap_camera_data
};
class SystemStatusView : public View {
public:
std::function<void(void)> on_back;
SystemStatusView();
void set_back_visible(bool new_value);
void set_back_enabled(bool new_value);
void set_title(const std::string new_value);
private:
static constexpr auto default_title = "PortaPack";
static constexpr auto back_text_enabled = " < ";
static constexpr auto back_text_disabled = " * ";
Button button_back {
{ 0 * 8, 0 * 16, 3 * 8, 16 },
" < ",
back_text_disabled,
};
Text title {
@ -82,6 +107,13 @@ private:
default_title,
};
ImageButton button_camera {
{ 22 * 8, 0, 2 * 8, 1 * 16 },
&bitmap_camera,
Color::white(),
Color::black()
};
ImageButton button_sleep {
{ 25 * 8, 0, 2 * 8, 1 * 16 },
&bitmap_sleep,
@ -92,6 +124,8 @@ private:
SDCardStatusView sd_card_status_view {
{ 28 * 8, 0 * 16, 2 * 8, 1 * 16 }
};
void on_camera();
};
class NavigationView : public View {

View file

@ -38,7 +38,7 @@ FrequencyField::FrequencyField(
length_ { 11 },
range(rf::tuning_range)
{
flags.focusable = true;
set_focusable(true);
}
rf::Frequency FrequencyField::value() const {
@ -318,4 +318,24 @@ void LNAGainField::on_focus() {
}
}
/* VGAGainField **********************************************************/
VGAGainField::VGAGainField(
Point parent_pos
) : NumberField {
parent_pos, 2,
{ max2837::vga::gain_db_range.minimum, max2837::vga::gain_db_range.maximum },
max2837::vga::gain_db_step,
' ',
}
{
}
void VGAGainField::on_focus() {
//Widget::on_focus();
if( on_show_options ) {
on_show_options();
}
}
} /* namespace ui */

View file

@ -315,6 +315,15 @@ public:
void on_focus() override;
};
class VGAGainField : public NumberField {
public:
std::function<void(void)> on_show_options;
VGAGainField(Point parent_pos);
void on_focus() override;
};
} /* namespace ui */
#endif/*__UI_RECEIVER_H__*/

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[] = {
0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f,
0xf8, 0x1f, 0xf8, 0x1b, 0xf8, 0x19, 0xf8, 0x1c,
0xf8, 0x1e, 0xf8, 0x1c, 0xf8, 0x19, 0xf8, 0x1b,
0xf8, 0x1f, 0xd8, 0x1b, 0x98, 0x19, 0x38, 0x1c,
0x78, 0x1e, 0x38, 0x1c, 0x98, 0x19, 0xd8, 0x1b,
0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
};

View file

@ -85,7 +85,7 @@ void SetDateTimeView::focus() {
button_cancel.focus();
}
void SetDateTimeView::form_init(const SetDateTimeModel model) {
void SetDateTimeView::form_init(const SetDateTimeModel& model) {
field_year.set_value(model.year);
field_month.set_value(model.month);
field_day.set_value(model.day);
@ -137,7 +137,7 @@ void SetFrequencyCorrectionView::focus() {
button_cancel.focus();
}
void SetFrequencyCorrectionView::form_init(const SetFrequencyCorrectionModel model) {
void SetFrequencyCorrectionView::form_init(const SetFrequencyCorrectionModel& model) {
field_ppm.set_value(model.ppm);
}

View file

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

View file

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