Merge remote-tracking branch 'upstream/master'

Conflicts:
	firmware/application/Makefile
	firmware/application/core_control.cpp
	firmware/application/touch.cpp
	firmware/application/ui_debug.cpp
	firmware/application/ui_debug.hpp
	firmware/application/ui_navigation.cpp
	firmware/baseband/baseband_thread.cpp
This commit is contained in:
furrtek 2016-04-21 20:36:19 +02:00
commit 1b0da68d65
252 changed files with 10752 additions and 7183 deletions

View File

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

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

@ -19,27 +19,21 @@
* Boston, MA 02110-1301, USA.
*/
#ifndef __AUDIO_H__
#define __AUDIO_H__
#include "audio.hpp"
#include "buffer.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 {
struct sample_t {
union {
struct {
int16_t left;
int16_t right;
};
uint32_t raw;
};
};
using buffer_t = buffer_t<sample_t>;
namespace {
constexpr i2s::ConfigTX i2s0_config_tx {
.dao = i2s::DAO {
@ -103,6 +97,78 @@ constexpr i2s::ConfigDMA i2s0_config_dma {
},
};
} /* namespace audio */
constexpr uint8_t wm8731_i2c_address = 0x1a;
#endif/*__AUDIO_H__*/
WM8731 audio_codec { i2c0, wm8731_i2c_address };
} /* namespace */
namespace output {
void start() {
i2s::i2s0::tx_start();
unmute();
}
void stop() {
mute();
i2s::i2s0::tx_stop();
}
void mute() {
i2s::i2s0::tx_mute();
audio_codec.headphone_mute();
}
void unmute() {
i2s::i2s0::tx_unmute();
}
} /* namespace output */
namespace headphone {
volume_range_t volume_range() {
return wolfson::wm8731::headphone_gain_range;
}
void set_volume(const volume_t volume) {
audio_codec.set_headphone_volume(volume);
}
} /* namespace headphone */
namespace debug {
int reg_count() {
return wolfson::wm8731::reg_count;
}
uint16_t reg_read(const int register_number) {
return audio_codec.read(register_number);
}
} /* namespace debug */
void init() {
clock_manager.start_audio_pll();
audio_codec.init();
i2s::i2s0::configure(
i2s0_config_tx,
i2s0_config_rx,
i2s0_config_dma
);
}
void shutdown() {
audio_codec.reset();
output::stop();
}
void set_rate(const Rate rate) {
clock_manager.set_base_audio_clock_divider(toUType(rate));
}
} /* namespace audio */

View File

@ -0,0 +1,69 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __AUDIO_H__
#define __AUDIO_H__
#include "volume.hpp"
#include <cstdint>
namespace audio {
namespace output {
void start();
void stop();
void mute();
void unmute();
} /* namespace output */
namespace headphone {
volume_range_t volume_range();
void set_volume(const volume_t volume);
} /* namespace headphone */
namespace debug {
int reg_count();
uint16_t reg_read(const int register_number);
} /* namespace debug */
void init();
void shutdown();
enum class Rate {
Hz_12000 = 4,
Hz_24000 = 2,
Hz_48000 = 1,
};
void set_rate(const Rate rate);
} /* namespace audio */
#endif/*__AUDIO_H__*/

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
@ -19,8 +19,6 @@
* Boston, MA 02110-1301, USA.
*/
#include "audio.hpp"
#include "audio_thread.hpp"
namespace audio {
} /* namespace audio */
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);

View File

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

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "audio_compressor.hpp"
float GainComputer::operator()(const float x) const {
const auto abs_x = std::abs(x);
const auto db = (abs_x < lin_floor) ? db_floor : log2_db_k * fast_log2(abs_x);
const auto overshoot_db = db - threshold_db;
if( knee_width_db > 0.0f ) {
const auto w2 = knee_width_db / 2.0f;
const auto a = w2 / (knee_width_db * knee_width_db);
const auto in_transition = (overshoot_db > -w2) && (overshoot_db < w2);
const auto rectified_overshoot = in_transition ? (a * std::pow(overshoot_db + w2, 2.0f)) : std::max(overshoot_db, 0.0f);
return rectified_overshoot * slope;
} else {
const auto rectified_overshoot = std::max(overshoot_db, 0.0f);
return rectified_overshoot * slope;
}
}
void FeedForwardCompressor::execute_in_place(const buffer_f32_t& buffer) {
constexpr float makeup_gain = std::pow(10.0f, (threshold - (threshold / ratio)) / -20.0f);
for(size_t i=0; i<buffer.count; i++) {
buffer.p[i] = execute_once(buffer.p[i]) * makeup_gain;
}
}
float FeedForwardCompressor::execute_once(const float x) {
const auto gain_db = gain_computer(x);
const auto peak_db = -peak_detector(-gain_db);
const auto gain = fast_pow2(peak_db * (3.321928094887362f / 20.0f));
return x * gain;
}

View File

@ -0,0 +1,102 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __AUDIO_COMPRESSOR_H__
#define __AUDIO_COMPRESSOR_H__
#include "dsp_types.hpp"
#include "utility.hpp"
#include <cmath>
/* Code based on article in Journal of the Audio Engineering Society
* Vol. 60, No. 6, 2012 June, by Dimitrios Giannoulis, Michael Massberg,
* Joshua D. Reiss "Digital Dynamic Range Compressor Design A Tutorial
* and Analysis"
*/
class GainComputer {
public:
constexpr GainComputer(
float ratio,
float threshold
) : ratio { ratio },
slope { 1.0f / ratio - 1.0f },
threshold_db { threshold }
{
}
float operator()(const float x) const;
private:
const float ratio;
const float slope;
const float threshold_db;
static constexpr float knee_width_db = 0.0f;
static constexpr float db_floor = -120.0f;
static constexpr float lin_floor = std::pow(10.0f, db_floor / 20.0f);
static constexpr float log2_db_k = 20.0f * std::log10(2.0f);
};
class PeakDetectorBranchingSmooth {
public:
constexpr PeakDetectorBranchingSmooth(
float att_a,
float rel_a
) : att_a { att_a },
rel_a { rel_a }
{
}
float operator()(const float db) {
const auto a = (db > state) ? att_a : rel_a;
state = db + a * (state - db);
return state;
}
private:
float state { 0.0f };
const float att_a;
const float rel_a;
};
class FeedForwardCompressor {
public:
void execute_in_place(const buffer_f32_t& buffer);
private:
static constexpr float fs = 12000.0f;
static constexpr float ratio = 10.0f;
static constexpr float threshold = -30.0f;
GainComputer gain_computer { ratio, threshold };
PeakDetectorBranchingSmooth peak_detector { tau_alpha(0.010f, fs), tau_alpha(0.300f, fs) };
float execute_once(const float x);
static constexpr float tau_alpha(const float tau, const float fs) {
return std::exp(-1.0f / (tau * fs));
}
};
#endif/*__AUDIO_COMPRESSOR_H__*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

@ -0,0 +1,93 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __OOK_HPP__
#define __OOK_HPP__
#include "phase_detector.hpp"
#include "phase_accumulator.hpp"
#include <cstdint>
#include <complex>
#include <algorithm>
#include <cmath>
class OOKSlicerMagSquaredInt {
public:
using symbol_t = bool;
constexpr OOKSlicerMagSquaredInt(
const float samples_per_symbol
) : mag2_threshold_leak_factor {
static_cast<uint32_t>(
factor_sq(-1.0f / (8.0f * samples_per_symbol)) * float(1ULL << 32)
)
}
{
}
symbol_t operator()(const std::complex<int16_t> in) {
const uint32_t real2 = in.real() * in.real();
const uint32_t imag2 = in.imag() * in.imag();
const uint32_t mag2 = real2 + imag2;
const uint32_t mag2_attenuated = mag2 >> 3; // Approximation of (-4.5dB)^2
mag2_threshold = (uint64_t(mag2_threshold) * uint64_t(mag2_threshold_leak_factor)) >> 32;
mag2_threshold = std::max(mag2_threshold, mag2_attenuated);
const bool symbol = (mag2 > mag2_threshold);
return symbol;
}
private:
const uint32_t mag2_threshold_leak_factor;
uint32_t mag2_threshold = 0;
constexpr float factor_sq(float db) {
return std::pow(10.0f, db / (10.0f / 2));
}
};
class OOKClockRecovery {
public:
constexpr OOKClockRecovery(
const float samples_per_symbol
) : symbol_phase_inc_nominal { static_cast<uint32_t>(std::round((1ULL << 32) / samples_per_symbol)) },
phase_detector { samples_per_symbol },
phase_accumulator { symbol_phase_inc_nominal }
{
}
template<typename SymbolHandler>
void operator()(const uint32_t slicer_history, SymbolHandler symbol_handler) {
if( phase_accumulator() ) {
const auto detector_result = phase_detector(slicer_history);
phase_accumulator.set_inc(symbol_phase_inc_nominal + detector_result.error * (symbol_phase_inc_nominal >> 3));
symbol_handler(detector_result.symbol);
}
}
private:
const uint32_t symbol_phase_inc_nominal;
PhaseDetectorEarlyLateGate phase_detector;
PhaseAccumulator phase_accumulator;
};
#endif/*__OOK_HPP__*/

View File

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

View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __PHASE_ACCUMULATOR_HPP__
#define __PHASE_ACCUMULATOR_HPP__
#include <cstdint>
class PhaseAccumulator {
public:
constexpr PhaseAccumulator(
const uint32_t phase_inc
) : phase_inc { phase_inc }
{
}
bool operator()() {
const auto last_phase = phase;
phase += phase_inc;
return (phase < last_phase);
}
void set_inc(const uint32_t new_phase_inc) {
phase_inc = new_phase_inc;
}
private:
uint32_t phase { 0 };
uint32_t phase_inc;
};
#endif/*__PHASE_ACCUMULATOR_HPP__*/

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __PHASE_DETECTOR_HPP__
#define __PHASE_DETECTOR_HPP__
#include <cstdint>
#include <cstddef>
#include <cmath>
class PhaseDetectorEarlyLateGate {
public:
using history_t = uint32_t;
using symbol_t = bool;
using error_t = int;
struct result_t {
symbol_t symbol;
error_t error;
};
constexpr PhaseDetectorEarlyLateGate(
const float samples_per_symbol
) : late_mask { (1U << static_cast<size_t>(std::ceil(samples_per_symbol / 2))) - 1 },
early_mask { late_mask << static_cast<size_t>(std::floor(samples_per_symbol / 2)) },
sample_bit { static_cast<size_t>(std::floor(samples_per_symbol / 2)) }
{
}
result_t operator()(const history_t symbol_history) const {
// history = ...0111, early
// history = ...1110, late
const symbol_t symbol = (symbol_history >> sample_bit) & 1;
const int late_side = __builtin_popcount(symbol_history & late_mask);
const int early_side = __builtin_popcount(symbol_history & early_mask);
const int lateness = late_side - early_side;
const int direction = lateness; //std::min(std::max(lateness, -1), 1);
const error_t error = direction;
return { symbol, error };
}
private:
const history_t late_mask;
const history_t early_mask;
const size_t sample_bit;
};
#endif/*__PHASE_DETECTOR_HPP__*/

View File

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

View File

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

View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "proc_capture.hpp"
#include "dsp_fir_taps.hpp"
#include "utility.hpp"
CaptureProcessor::CaptureProcessor() {
const auto& decim_0_filter = taps_200k_decim_0;
constexpr size_t decim_0_input_fs = baseband_fs;
constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0.decimation_factor;
const auto& decim_1_filter = taps_200k_decim_1;
constexpr size_t decim_1_input_fs = decim_0_output_fs;
constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
const auto& channel_filter = decim_1_filter;
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
constexpr size_t channel_decimation = 1;
const size_t channel_filter_output_fs = channel_filter_input_fs / channel_decimation;
decim_0.configure(decim_0_filter.taps, 33554432);
decim_1.configure(decim_1_filter.taps, 131072);
channel_filter_pass_f = channel_filter.pass_frequency_normalized * channel_filter_input_fs;
channel_filter_stop_f = channel_filter.stop_frequency_normalized * channel_filter_input_fs;
spectrum_interval_samples = channel_filter_output_fs / spectrum_rate_hz;
spectrum_samples = 0;
channel_spectrum.set_decimation_factor(1);
stream = std::make_unique<StreamInput>(15);
}
void CaptureProcessor::execute(const buffer_c8_t& buffer) {
/* 2.4576MHz, 2048 samples */
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
const auto& decimator_out = decim_1_out;
const auto& channel = decimator_out;
if( stream ) {
const size_t bytes_to_write = sizeof(*decimator_out.p) * decimator_out.count;
const auto result = stream->write(decimator_out.p, bytes_to_write);
}
feed_channel_stats(channel);
spectrum_samples += channel.count;
if( spectrum_samples >= spectrum_interval_samples ) {
spectrum_samples -= spectrum_interval_samples;
channel_spectrum.feed(channel, channel_filter_pass_f, channel_filter_stop_f);
}
}
void CaptureProcessor::on_message(const Message* const message) {
switch(message->id) {
case Message::ID::UpdateSpectrum:
case Message::ID::SpectrumStreamingConfig:
channel_spectrum.on_message(message);
break;
default:
break;
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __PROC_CAPTURE_HPP__
#define __PROC_CAPTURE_HPP__
#include "baseband_processor.hpp"
#include "dsp_decimate.hpp"
#include "spectrum_collector.hpp"
#include "stream_input.hpp"
#include <array>
#include <memory>
class CaptureProcessor : public BasebandProcessor {
public:
CaptureProcessor();
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 2457600;
static constexpr auto spectrum_rate_hz = 50.0f;
std::array<complex16_t, 512> dst;
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0;
dsp::decimate::FIRC16xR16x16Decim2 decim_1;
uint32_t channel_filter_pass_f = 0;
uint32_t channel_filter_stop_f = 0;
std::unique_ptr<StreamInput> stream;
SpectrumCollector channel_spectrum;
size_t spectrum_interval_samples = 0;
size_t spectrum_samples = 0;
};
#endif/*__PROC_CAPTURE_HPP__*/

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,73 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __STREAM_INPUT_H__
#define __STREAM_INPUT_H__
#include "portapack_shared_memory.hpp"
#include "fifo.hpp"
#include <cstdint>
#include <cstddef>
#include <memory>
class StreamInput {
public:
StreamInput(const size_t K) :
K { K },
data { std::make_unique<uint8_t[]>(1UL << K) },
fifo { data.get(), K }
{
// TODO: Send stream creation message.
shared_memory.FIFO_HACK = &fifo;
}
~StreamInput() {
// TODO: Send stream distruction message.
shared_memory.FIFO_HACK = nullptr;
}
size_t write(const void* const data, const size_t length) {
const auto written = fifo.in(reinterpret_cast<const uint8_t*>(data), length);
const auto last_bytes_written = bytes_written;
bytes_written += written;
if( (bytes_written & event_bytes_mask) < (last_bytes_written & event_bytes_mask) ) {
creg::m4txevent::assert();
}
return written;
}
uint64_t written() const {
return bytes_written;
}
private:
const size_t K;
const uint64_t event_bytes_mask = (1ULL << (K - 2)) - 1;
uint64_t bytes_written = 0;
std::unique_ptr<uint8_t[]> data;
FIFO<uint8_t> fifo;
};
#endif/*__STREAM_INPUT_H__*/

View File

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

View File

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

View File

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

View File

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

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