This commit is contained in:
furrtek 2016-05-09 21:05:11 +02:00
parent 8523bd860e
commit 569f299f42
57 changed files with 1155 additions and 772 deletions

View File

@ -14,9 +14,9 @@ notifications:
- "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
- wget https://launchpad.net/gcc-arm-embedded/5.0/5-2016-q1-update/+download/gcc-arm-none-eabi-5_3-2016q1-20160330-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 PATH=$PWD/gcc-arm-none-eabi-5_3-2016q1/bin:$PATH
- export CC="arm-none-eabi-gcc"
- export CXX="arm-none-eabi-g++"

View File

@ -175,6 +175,7 @@ CPPSRC = main.cpp \
ui_sd_card_debug.cpp \
ui_console.cpp \
ui_receiver.cpp \
ui_record_view.cpp \
ui_spectrum.cpp \
ui_loadmodule.cpp \
ui_afskrx.cpp \
@ -196,10 +197,11 @@ CPPSRC = main.cpp \
../common/ert_packet.cpp \
capture_app.cpp \
sd_card.cpp \
time.cpp \
file.cpp \
log_file.cpp \
png_writer.cpp \
audio_thread.cpp \
capture_thread.cpp \
manchester.cpp \
string_format.cpp \
temperature_logger.cpp \

View File

@ -136,7 +136,7 @@ AISLogger::AISLogger(
void AISLogger::on_packet(const ais::Packet& packet) {
// TODO: Unstuff here, not in baseband!
if( log_file.is_ready() ) {
if( log_file.is_open() ) {
std::string entry;
entry.reserve((packet.length() + 3) / 4);

View File

@ -30,6 +30,8 @@ using namespace portapack;
#include "utility.hpp"
#include "string_format.hpp"
namespace ui {
/* AMOptionsView *********************************************************/
@ -84,6 +86,7 @@ AnalogAudioView::AnalogAudioView(
&field_vga,
&options_modulation,
&field_volume,
&record_view,
&waterfall,
} });
@ -208,6 +211,8 @@ void AnalogAudioView::remove_options_widget() {
}
void AnalogAudioView::set_options_widget(std::unique_ptr<Widget> new_widget) {
remove_options_widget();
if( new_widget ) {
options_widget = std::move(new_widget);
add_child(options_widget.get());
@ -215,11 +220,6 @@ void AnalogAudioView::set_options_widget(std::unique_ptr<Widget> new_widget) {
}
void AnalogAudioView::on_show_options_frequency() {
// TODO: This approach of managing options views is error-prone and unsustainable!
remove_options_widget();
field_frequency.set_style(&style_options_group);
auto widget = std::make_unique<FrequencyOptionsView>(options_view_rect, &style_options_group);
widget->set_step(receiver_model.frequency_step());
@ -232,14 +232,10 @@ void AnalogAudioView::on_show_options_frequency() {
};
set_options_widget(std::move(widget));
field_frequency.set_style(&style_options_group);
}
void AnalogAudioView::on_show_options_rf_gain() {
// TODO: This approach of managing options views is error-prone and unsustainable!
remove_options_widget();
field_lna.set_style(&style_options_group);
auto widget = std::make_unique<RadioGainOptionsView>(options_view_rect, &style_options_group);
widget->set_rf_amp(receiver_model.rf_amp());
@ -248,23 +244,28 @@ void AnalogAudioView::on_show_options_rf_gain() {
};
set_options_widget(std::move(widget));
field_lna.set_style(&style_options_group);
}
void AnalogAudioView::on_show_options_modulation() {
// TODO: This approach of managing options views is error-prone and unsustainable!
remove_options_widget();
std::unique_ptr<Widget> widget;
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>(options_view_rect, &style_options_group);
set_options_widget(std::move(widget));
switch(modulation) {
case ReceiverModel::Mode::AMAudio:
widget = std::make_unique<AMOptionsView>(options_view_rect, &style_options_group);
break;
case ReceiverModel::Mode::NarrowbandFMAudio:
widget = std::make_unique<NBFMOptionsView>(options_view_rect, &style_options_group);
break;
default:
break;
}
if( modulation == ReceiverModel::Mode::NarrowbandFMAudio ) {
options_modulation.set_style(&style_options_group);
auto widget = std::make_unique<NBFMOptionsView>(options_view_rect, &style_options_group);
set_options_widget(std::move(widget));
}
options_modulation.set_style(&style_options_group);
}
void AnalogAudioView::on_frequency_step_changed(rf::Frequency f) {
@ -283,7 +284,7 @@ void AnalogAudioView::on_headphone_volume_changed(int32_t v) {
void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
audio::output::mute();
audio_thread.reset();
record_view.stop();
const auto is_wideband_spectrum_mode = (modulation == ReceiverModel::Mode::SpectrumAnalysis);
receiver_model.set_baseband_configuration({
@ -294,11 +295,18 @@ 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);
// TODO: This doesn't belong here! There's a better way.
size_t sampling_rate = 0;
switch(modulation) {
case ReceiverModel::Mode::AMAudio: sampling_rate = 12000; break;
case ReceiverModel::Mode::NarrowbandFMAudio: sampling_rate = 24000; break;
case ReceiverModel::Mode::WidebandFMAudio: sampling_rate = 48000; break;
default:
break;
}
record_view.set_sampling_rate(sampling_rate);
if( !is_wideband_spectrum_mode ) {
audio::output::unmute();
}
}

View File

@ -26,8 +26,7 @@
#include "ui_receiver.hpp"
#include "ui_spectrum.hpp"
#include "audio_thread.hpp"
#include "ui_record_view.hpp"
#include "ui_font_fixed_8x16.hpp"
@ -93,7 +92,7 @@ public:
void focus() override;
private:
static constexpr ui::Dim header_height = 2 * 16;
static constexpr ui::Dim header_height = 3 * 16;
const Rect options_view_rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 };
@ -142,9 +141,12 @@ private:
std::unique_ptr<Widget> options_widget;
spectrum::WaterfallWidget waterfall;
RecordView record_view {
{ 0 * 8, 2 * 16, 30 * 8, 1 * 16 },
"AUD_????", RecordView::FileType::WAV, 12, 2,
};
std::unique_ptr<AudioThread> audio_thread;
spectrum::WaterfallWidget waterfall;
void on_tuning_frequency_changed(rf::Frequency f);
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);

View File

@ -1,145 +0,0 @@
/*
* 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,156 @@
/*
* 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.
*/
#ifndef __BITMAP_HPP__
#define __BITMAP_HPP__
#include "ui.hpp"
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
};
static constexpr uint8_t bitmap_sleep_data[] = {
0x00, 0x00,
0x00, 0x00,
0x00, 0x04,
0x00, 0x08,
0x00, 0x18,
0x00, 0x18,
0x00, 0x38,
0x00, 0x3c,
0x00, 0x3c,
0x00, 0x3e,
0x84, 0x1f,
0xf8, 0x1f,
0xf0, 0x0f,
0xc0, 0x03,
0x00, 0x00,
0x00, 0x00,
};
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
};
static constexpr uint8_t bitmap_sd_card_ok_data[] = {
0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f,
0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
};
static constexpr Bitmap bitmap_sd_card_ok {
{ 16, 16 }, bitmap_sd_card_ok_data
};
static constexpr uint8_t bitmap_sd_card_unknown_data[] = {
0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f,
0x38, 0x1c, 0x98, 0x19, 0xf8, 0x19, 0xf8, 0x1c,
0x78, 0x1e, 0x78, 0x1e, 0xf8, 0x1f, 0x78, 0x1e,
0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
};
static constexpr Bitmap bitmap_sd_card_unknown {
{ 16, 16 }, bitmap_sd_card_unknown_data
};
static constexpr uint8_t bitmap_sd_card_error_data[] = {
0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f,
0xf8, 0x1f, 0xd8, 0x1b, 0x98, 0x19, 0x38, 0x1c,
0x78, 0x1e, 0x38, 0x1c, 0x98, 0x19, 0xd8, 0x1b,
0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
};
static constexpr Bitmap bitmap_sd_card_error {
{ 16, 16 }, bitmap_sd_card_error_data
};
} /* namespace ui */
#endif/*__BITMAP_HPP__*/

View File

@ -24,20 +24,16 @@
#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,
&record_view,
&waterfall,
} });
@ -65,10 +61,6 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
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,
@ -76,6 +68,8 @@ CaptureAppView::CaptureAppView(NavigationView& nav) {
});
receiver_model.set_baseband_bandwidth(baseband_bandwidth);
receiver_model.enable();
record_view.set_sampling_rate(sampling_rate / 8);
}
CaptureAppView::~CaptureAppView() {
@ -97,22 +91,7 @@ void CaptureAppView::set_parent_rect(const Rect new_parent_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);
}
record_view.focus();
}
void CaptureAppView::on_tuning_frequency_changed(rf::Frequency f) {

View File

@ -25,61 +25,14 @@
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_record_view.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);
@ -94,26 +47,15 @@ public:
std::string title() const override { return "Capture"; };
private:
static constexpr ui::Dim header_height = 2 * 16;
static constexpr ui::Dim header_height = 3 * 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 },
};
@ -134,6 +76,11 @@ private:
{ 18 * 8, 0 * 16 }
};
RecordView record_view {
{ 0 * 8, 2 * 16, 30 * 8, 1 * 16 },
"BBD_????", RecordView::FileType::RawS16, 14, 1,
};
spectrum::WaterfallWidget waterfall;
};

View File

@ -0,0 +1,122 @@
/*
* 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_thread.hpp"
#include "portapack_shared_memory.hpp"
// StreamOutput ///////////////////////////////////////////////////////////
class StreamOutput {
public:
StreamOutput(CaptureConfig* const config);
~StreamOutput();
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);
}
static FIFO<uint8_t>* fifo;
private:
CaptureConfig* const config;
};
FIFO<uint8_t>* StreamOutput::fifo = nullptr;
StreamOutput::StreamOutput(
CaptureConfig* const config
) : config { config }
{
shared_memory.baseband_queue.push_and_wait(
CaptureConfigMessage { config }
);
fifo = config->fifo;
}
StreamOutput::~StreamOutput() {
fifo = nullptr;
shared_memory.baseband_queue.push_and_wait(
CaptureConfigMessage { nullptr }
);
}
// CaptureThread //////////////////////////////////////////////////////////
Thread* CaptureThread::thread = nullptr;
CaptureThread::CaptureThread(
std::unique_ptr<Writer> writer,
size_t write_size_log2,
size_t buffer_count_log2
) : config { write_size_log2, buffer_count_log2 },
writer { std::move(writer) }
{
// Need significant stack for FATFS
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, CaptureThread::static_fn, this);
}
CaptureThread::~CaptureThread() {
if( thread ) {
chThdTerminate(thread);
chEvtSignal(thread, EVT_MASK_CAPTURE_THREAD);
chThdWait(thread);
thread = nullptr;
}
}
void CaptureThread::check_fifo_isr() {
// TODO: Prevent over-signalling by transmitting a set of
// flags from the baseband core.
const auto fifo = StreamOutput::fifo;
if( fifo ) {
chEvtSignalI(thread, EVT_MASK_CAPTURE_THREAD);
}
}
msg_t CaptureThread::run() {
const size_t write_size = 1U << config.write_size_log2;
const auto write_buffer = std::make_unique<uint8_t[]>(write_size);
if( !write_buffer ) {
return false;
}
StreamOutput stream { &config };
while( !chThdShouldTerminate() ) {
if( stream.available() >= write_size ) {
if( stream.read(write_buffer.get(), write_size) != write_size ) {
return false;
}
if( !writer->write(write_buffer.get(), write_size) ) {
return false;
}
} else {
chEvtWaitAny(EVT_MASK_CAPTURE_THREAD);
}
}
return true;
}

View File

@ -0,0 +1,67 @@
/*
* 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_THREAD_H__
#define __CAPTURE_THREAD_H__
#include "ch.h"
#include "event_m0.hpp"
#include <cstdint>
#include <cstddef>
#include <utility>
class Writer {
public:
virtual bool write(const void* const buffer, const size_t bytes) = 0;
virtual ~Writer() = default;
};
class CaptureThread {
public:
CaptureThread(
std::unique_ptr<Writer> writer,
size_t write_size_log2,
size_t buffer_count_log2
);
~CaptureThread();
const CaptureConfig& state() const {
return config;
}
static void check_fifo_isr();
private:
CaptureConfig config;
std::unique_ptr<Writer> writer;
static Thread* thread;
static msg_t static_fn(void* arg) {
auto obj = static_cast<CaptureThread*>(arg);
return obj->run();
}
msg_t run();
};
#endif/*__CAPTURE_THREAD_H__*/

View File

@ -66,7 +66,7 @@ ERTLogger::ERTLogger(
}
void ERTLogger::on_packet(const ert::Packet& packet) {
if( log_file.is_ready() ) {
if( log_file.is_open() ) {
const auto formatted = packet.symbols_formatted();
log_file.write_entry(packet.received_at(), formatted.data + "/" + formatted.errors);
}

View File

@ -22,16 +22,17 @@
#include "event_m0.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
#include "portapack_shared_memory.hpp"
#include "sd_card.hpp"
#include "time.hpp"
#include "message.hpp"
#include "message_queue.hpp"
#include "irq_controls.hpp"
#include "audio_thread.hpp"
#include "capture_thread.hpp"
#include "ch.h"
@ -46,8 +47,8 @@ CH_IRQ_HANDLER(M4Core_IRQHandler) {
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
AudioThread::check_fifo_isr();
EventDispatcher::events_flag_isr(EVT_MASK_APPLICATION);
CaptureThread::check_fifo_isr();
EventDispatcher::check_fifo_isr();
chSysUnlockFromIsr();
creg::m4txevent::clear();
@ -132,6 +133,10 @@ void EventDispatcher::dispatch(const eventmask_t events) {
handle_lcd_frame_sync();
}
if( events & EVT_MASK_ENCODER ) {
handle_encoder();
}
if( events & EVT_MASK_TOUCH ) {
handle_touch();
}
@ -158,6 +163,8 @@ void EventDispatcher::handle_rtc_tick() {
else
portapack::bl_tick_counter++;
}
time::on_tick_second();
}
ui::Widget* EventDispatcher::touch_widget(ui::Widget* const w, ui::TouchEvent event) {
@ -278,6 +285,4 @@ void EventDispatcher::init_message_queues() {
new (&shared_memory.application_queue) MessageQueue(
shared_memory.application_queue_data, SharedMemory::application_queue_k
);
shared_memory.FIFO_HACK = nullptr;
}

View File

@ -28,6 +28,7 @@
#include "ui_painter.hpp"
#include "portapack.hpp"
#include "portapack_shared_memory.hpp"
#include "message.hpp"
@ -43,6 +44,7 @@ constexpr auto EVT_MASK_SWITCHES = EVENT_MASK(3);
constexpr auto EVT_MASK_ENCODER = EVENT_MASK(4);
constexpr auto EVT_MASK_TOUCH = EVENT_MASK(5);
constexpr auto EVT_MASK_APPLICATION = EVENT_MASK(6);
constexpr auto EVT_MASK_CAPTURE_THREAD = EVENT_MASK(7);
class EventDispatcher {
public:
@ -57,6 +59,12 @@ public:
void set_display_sleep(const bool sleep);
static inline void check_fifo_isr() {
if( !shared_memory.application_queue.is_empty() ) {
events_flag_isr(EVT_MASK_APPLICATION);
}
}
static inline void events_flag(const eventmask_t events) {
if( thread_event_loop ) {
chEvtSignal(thread_event_loop, events);
@ -73,8 +81,6 @@ public:
return message_map_;
}
static Thread* thread_record;
private:
static MessageHandlerMap message_map_;
static Thread* thread_event_loop;

View File

@ -23,40 +23,32 @@
#include <algorithm>
File::~File() {
close();
}
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);
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;
} else {
close();
File::File(const std::string& filename, openmode mode) {
BYTE fatfs_mode = 0;
if( mode & openmode::in ) {
fatfs_mode |= FA_READ;
}
if( mode & openmode::out ) {
fatfs_mode |= FA_WRITE;
}
if( mode & openmode::trunc ) {
fatfs_mode |= FA_CREATE_ALWAYS;
}
if( mode & openmode::ate ) {
fatfs_mode |= FA_OPEN_ALWAYS;
}
return false;
}
bool File::close() {
if( f_open(&f, filename.c_str(), fatfs_mode) == FR_OK ) {
if( mode & openmode::ate ) {
if( f_lseek(&f, f_size(&f)) != FR_OK ) {
f_close(&f);
return true;
}
}
}
}
bool File::is_ready() {
return f_error(&f) == 0;
File::~File() {
f_close(&f);
}
bool File::read(void* const data, const size_t bytes_to_read) {
@ -71,6 +63,17 @@ bool File::write(const void* const data, const size_t bytes_to_write) {
return (result == FR_OK) && (bytes_written == bytes_to_write);
}
uint64_t File::seek(const uint64_t new_position) {
const auto old_position = f_tell(&f);
if( f_lseek(&f, new_position) != FR_OK ) {
f_close(&f);
}
if( f_tell(&f) != new_position ) {
f_close(&f);
}
return old_position;
}
bool File::puts(const std::string& string) {
const auto result = f_puts(string.c_str(), &f);
return (result >= 0);
@ -94,22 +97,16 @@ static std::string find_last_file_matching_pattern(const std::string& pattern) {
return last_match;
}
static std::string increment_filename_ordinal(const std::string& filename) {
std::string result { filename };
static std::string remove_filename_extension(const std::string& filename) {
const auto extension_index = filename.find_last_of('.');
return filename.substr(0, extension_index);
}
static std::string increment_filename_stem_ordinal(const std::string& filename_stem) {
std::string result { filename_stem };
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;
@ -128,15 +125,16 @@ static std::string increment_filename_ordinal(const std::string& filename) {
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');
std::string next_filename_stem_matching_pattern(const std::string& filename_stem_pattern) {
const auto filename = find_last_file_matching_pattern(filename_stem_pattern + ".*");
auto filename_stem = remove_filename_extension(filename);
if( filename_stem.empty() ) {
filename_stem = filename_stem_pattern;
std::replace(std::begin(filename_stem), std::end(filename_stem), '?', '0');
} else {
filename = increment_filename_ordinal(filename);
filename_stem = increment_filename_stem_ordinal(filename_stem);
}
return filename;
return filename_stem;
}
namespace std {

View File

@ -32,18 +32,27 @@
class File {
public:
enum openmode {
app = 0x100,
binary = 0x200,
in = FA_READ,
out = FA_WRITE,
trunc = FA_CREATE_ALWAYS,
ate = FA_OPEN_ALWAYS,
};
File(const std::string& filename, openmode mode);
~File();
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();
bool is_open() const {
return f_error(&f) == 0;
}
bool read(void* const data, const size_t bytes_to_read);
bool write(const void* const data, const size_t bytes_to_write);
uint64_t seek(const uint64_t new_position);
template<size_t N>
bool write(const std::array<uint8_t, N>& data) {
return write(data.data(), N);
@ -57,7 +66,11 @@ private:
FIL f;
};
std::string next_filename_matching_pattern(const std::string& filename_pattern);
inline constexpr File::openmode operator|(File::openmode a, File::openmode b) {
return File::openmode(static_cast<int>(a) | static_cast<int>(b));
}
std::string next_filename_stem_matching_pattern(const std::string& filename_stem_pattern);
namespace std {
namespace filesystem {

View File

@ -23,28 +23,14 @@
#include "string_format.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
LogFile::LogFile(
const std::string& file_path
) : file_path { file_path }
) : file { file_path, File::openmode::out | File::openmode::ate }
{
file.open_for_append(file_path);
sd_card_status_signal_token = sd_card::status_signal += [this](const sd_card::Status status) {
this->on_sd_card_status(status);
};
}
LogFile::~LogFile() {
sd_card::status_signal -= sd_card_status_signal_token;
file.close();
}
bool LogFile::is_ready() {
return file.is_ready();
bool LogFile::is_open() const {
return file.is_open();
}
bool LogFile::write_entry(const rtc::RTC& datetime, const std::string& entry) {
@ -55,11 +41,3 @@ bool LogFile::write_entry(const rtc::RTC& datetime, const std::string& entry) {
bool LogFile::write(const std::string& message) {
return file.puts(message) && file.sync();
}
void LogFile::on_sd_card_status(const sd_card::Status status) {
if( status == sd_card::Status::Mounted ) {
file.open_for_append(file_path);
} else {
file.close();
}
}

View File

@ -25,7 +25,6 @@
#include <string>
#include "file.hpp"
#include "sd_card.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
@ -33,22 +32,15 @@ using namespace lpc43xx;
class LogFile {
public:
LogFile(const std::string& file_path);
~LogFile();
bool is_ready();
bool is_open() const;
bool write_entry(const rtc::RTC& datetime, const std::string& entry);
private:
const std::string file_path;
File file;
SignalToken sd_card_status_signal_token;
bool write(const std::string& message);
void on_sd_card_status(const sd_card::Status status);
};
#endif/*__LOG_FILE_H__*/

View File

@ -110,9 +110,23 @@ void init() {
}
/* Configure other pins */
/* Glitch filter operates at 3ns instead of 50ns due to the WM8731
* returning an ACK very fast (170ns) and confusing the I2C state
* machine into thinking there was a bus error. It looks like the
* MCU sees SDA fall before SCL falls, indicating a START at the
* point an ACK is expected. With the glitch filter off or set to
* 3ns, it's probably still a bit tight timing-wise, but improves
* reliability on some problem units.
*/
LPC_SCU->SFSI2C0 =
(1U << 3)
| (1U << 11)
(1U << 0) // SCL: 3ns glitch
| (0U << 2) // SCL: Standard/Fast mode
| (1U << 3) // SCL: Input enabled
| (0U << 7) // SCL: Enable input glitch filter
| (1U << 8) // SDA: 3ns glitch
| (0U << 10) // SDA: Standard/Fast mode
| (1U << 11) // SDA: Input enabled
| (0U << 15) // SDA: Enable input glitch filter
;
power.init();

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
@ -19,6 +19,14 @@
* Boston, MA 02110-1301, USA.
*/
#include "audio_thread.hpp"
#include "time.hpp"
Thread* AudioThread::thread = nullptr;
namespace time {
Signal<> signal_tick_second;
void on_tick_second() {
signal_tick_second.emit();
}
} /* namespace time */

View File

@ -0,0 +1,35 @@
/*
* 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.
*/
#ifndef __TIME_H__
#define __TIME_H__
#include "signal.hpp"
namespace time {
extern Signal<> signal_tick_second;
void on_tick_second();
} /* namespace time */
#endif/*__TIME_H__*/

View File

@ -62,7 +62,7 @@ TPMSLogger::TPMSLogger(
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() ) {
if( log_file.is_open() ) {
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
const auto tuning_frequency_str = to_string_dec_uint(target_frequency, 10);

View File

@ -34,7 +34,7 @@ namespace ui {
class Audio : public Widget {
public:
constexpr Audio(
Audio(
const Rect parent_rect
) : Widget { parent_rect },
rms_db_ { -120 },

View File

@ -34,7 +34,7 @@ namespace ui {
class Channel : public Widget {
public:
constexpr Channel(
Channel(
const Rect parent_rect
) : Widget { parent_rect },
max_db_ { -120 }

View File

@ -84,7 +84,7 @@ private:
class TemperatureWidget : public Widget {
public:
explicit constexpr TemperatureWidget(
explicit TemperatureWidget(
Rect parent_rect
) : Widget { parent_rect }
{

View File

@ -90,12 +90,12 @@ 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() ) {
const auto filename_stem = next_filename_stem_matching_pattern("SCR_????");
if( filename_stem.empty() ) {
return;
}
PNGWriter png { filename };
PNGWriter png { filename_stem + ".PNG" };
for(int i=0; i<320; i++) {
std::array<ColorRGB888, 240> row;

View File

@ -32,57 +32,13 @@
#include "ui_audio.hpp"
#include "ui_sd_card_status_view.hpp"
#include "bitmap.hpp"
#include <vector>
#include <utility>
namespace ui {
static constexpr uint8_t bitmap_sleep_data[] = {
0x00, 0x00,
0x00, 0x00,
0x00, 0x04,
0x00, 0x08,
0x00, 0x18,
0x00, 0x18,
0x00, 0x38,
0x00, 0x3c,
0x00, 0x3c,
0x00, 0x3e,
0x84, 0x1f,
0xf8, 0x1f,
0xf0, 0x0f,
0xc0, 0x03,
0x00, 0x00,
0x00, 0x00,
};
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;

View File

@ -0,0 +1,251 @@
/*
* 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_record_view.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "file.hpp"
#include "time.hpp"
#include "string_format.hpp"
#include "utility.hpp"
#include <cstdint>
class RawFileWriter : public Writer {
public:
RawFileWriter(
const std::string& filename
) : file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc }
{
}
bool write(const void* const buffer, const size_t bytes) override {
return file.write(buffer, bytes);
}
private:
File file;
};
class WAVFileWriter : public Writer {
public:
WAVFileWriter(
const std::string& filename,
size_t sampling_rate
) : file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc },
header { sampling_rate }
{
update_header();
}
~WAVFileWriter() {
update_header();
}
bool write(const void* const buffer, const size_t bytes) override {
const auto success = file.write(buffer, bytes) ;
if( success ) {
bytes_written += bytes;
}
return success;
}
private:
struct fmt_pcm_t {
constexpr fmt_pcm_t(
const uint32_t sampling_rate
) : nSamplesPerSec { sampling_rate },
nAvgBytesPerSec { nSamplesPerSec * nBlockAlign }
{
}
private:
const uint8_t ckID[4] { 'f', 'm', 't', ' ' };
const uint32_t cksize { 16 };
const uint16_t wFormatTag { 0x0001 };
const uint16_t nChannels { 1 };
const uint32_t nSamplesPerSec;
const uint32_t nAvgBytesPerSec;
const uint16_t nBlockAlign { 2 };
const uint16_t wBitsPerSample { 16 };
};
struct data_t {
void set_size(const uint32_t value) {
cksize = value;
}
private:
const uint8_t ckID[4] { 'd', 'a', 't', 'a' };
uint32_t cksize { 0 };
};
struct header_t {
constexpr header_t(
const uint32_t sampling_rate
) : fmt { sampling_rate }
{
}
void set_data_size(const uint32_t value) {
data.set_size(value);
cksize = sizeof(header_t) + value - 8;
}
private:
const uint8_t riff_id[4] { 'R', 'I', 'F', 'F' };
uint32_t cksize { 0 };
const uint8_t wave_id[4] { 'W', 'A', 'V', 'E' };
fmt_pcm_t fmt;
data_t data;
};
File file;
header_t header;
uint64_t bytes_written { 0 };
void update_header() {
header.set_data_size(bytes_written);
const auto old_position = file.seek(0);
file.write(&header, sizeof(header));
file.seek(old_position);
}
};
namespace ui {
RecordView::RecordView(
const Rect parent_rect,
std::string filename_stem_pattern,
const FileType file_type,
const size_t buffer_size_k,
const size_t buffer_count_k
) : View { parent_rect },
filename_stem_pattern { filename_stem_pattern },
file_type { file_type },
buffer_size_k { buffer_size_k },
buffer_count_k { buffer_count_k }
{
add_children({ {
&button_record,
&text_record_filename,
&text_record_dropped,
} });
button_record.on_select = [this](ImageButton&) {
this->toggle();
};
signal_token_tick_second = time::signal_tick_second += [this]() {
this->on_tick_second();
};
}
RecordView::~RecordView() {
time::signal_tick_second -= signal_token_tick_second;
}
void RecordView::focus() {
button_record.focus();
}
bool RecordView::is_active() const {
return (bool)capture_thread;
}
void RecordView::toggle() {
if( is_active() ) {
stop();
} else {
start();
}
}
void RecordView::start() {
stop();
text_record_filename.set("");
text_record_dropped.set("");
if( sampling_rate == 0 ) {
return;
}
const auto filename_stem = next_filename_stem_matching_pattern(filename_stem_pattern);
if( filename_stem.empty() ) {
return;
}
std::unique_ptr<Writer> writer;
switch(file_type) {
case FileType::WAV:
writer = std::make_unique<WAVFileWriter>(
filename_stem + ".WAV",
sampling_rate
);
break;
case FileType::RawS16:
write_metadata_file(filename_stem + ".TXT");
writer = std::make_unique<RawFileWriter>(
filename_stem + ".C16"
);
break;
default:
break;
};
if( writer ) {
text_record_filename.set(filename_stem);
button_record.set_bitmap(&bitmap_stop);
capture_thread = std::make_unique<CaptureThread>(
std::move(writer),
buffer_size_k, buffer_count_k
);
}
}
void RecordView::stop() {
if( is_active() ) {
capture_thread.reset();
button_record.set_bitmap(&bitmap_record);
}
}
void RecordView::write_metadata_file(const std::string& filename) {
File file { filename, File::openmode::out | File::openmode::trunc };
file.puts("sample_rate=" + to_string_dec_uint(sampling_rate) + "\n");
file.puts("center_frequency=" + to_string_dec_uint(receiver_model.tuning_frequency()) + "\n");
}
void RecordView::on_tick_second() {
if( is_active() ) {
const auto dropped_percent = std::min(99U, capture_thread->state().dropped_percent());
const auto s = to_string_dec_uint(dropped_percent, 2, ' ') + "\%";
text_record_dropped.set(s);
}
}
} /* namespace ui */

View File

@ -0,0 +1,103 @@
/*
* 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_RECORD_VIEW_H__
#define __UI_RECORD_VIEW_H__
#include "ui_widget.hpp"
#include "capture_thread.hpp"
#include "signal.hpp"
#include "bitmap.hpp"
#include <cstddef>
#include <string>
#include <memory>
namespace ui {
class RecordView : public View {
public:
enum FileType {
RawS16 = 2,
WAV = 3,
};
RecordView(
const Rect parent_rect,
std::string filename_stem_pattern,
FileType file_type,
const size_t buffer_size_k,
const size_t buffer_count_k
);
~RecordView();
void focus() override;
void set_sampling_rate(const size_t new_sampling_rate) {
if( new_sampling_rate != sampling_rate ) {
stop();
sampling_rate = new_sampling_rate;
}
}
void start();
void stop();
bool is_active() const;
private:
void toggle();
void write_metadata_file(const std::string& filename);
void on_tick_second();
const std::string filename_stem_pattern;
const FileType file_type;
const size_t buffer_size_k;
const size_t buffer_count_k;
size_t sampling_rate { 0 };
SignalToken signal_token_tick_second;
ImageButton button_record {
{ 0 * 8, 0 * 16, 2 * 8, 1 * 16 },
&bitmap_record,
Color::red(),
Color::black()
};
Text text_record_filename {
{ 3 * 8, 0 * 16, 8 * 8, 16 },
"",
};
Text text_record_dropped {
{ 16 * 8, 0 * 16, 3 * 8, 16 },
"",
};
std::unique_ptr<CaptureThread> capture_thread;
};
} /* namespace ui */
#endif/*__UI_RECORD_VIEW_H__*/

View File

@ -34,7 +34,7 @@ namespace ui {
class RSSI : public Widget {
public:
constexpr RSSI(
RSSI(
const Rect parent_rect
) : Widget { parent_rect },
min_ { 0 },

View File

@ -133,15 +133,15 @@ private:
return Result::FailHeap;
}
File file;
if( !file.open_for_writing(filename) ) {
File file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc };
if( !file.is_open() ) {
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) ) {
while( !chThdShouldTerminate() && file.is_open() && (_stats.write_bytes < bytes_to_write) ) {
lfsr_fill(v,
reinterpret_cast<lfsr_word_t*>(buffer->data()),
sizeof(*buffer.get()) / sizeof(lfsr_word_t)
@ -164,7 +164,7 @@ private:
}
}
file.close();
file.sync();
const halrtcnt_t test_end = halGetCounterValue();
_stats.write_test_duration = test_end - test_start;
@ -178,15 +178,15 @@ private:
return Result::FailHeap;
}
File file;
if( !file.open_for_reading(filename) ) {
File file { filename, File::openmode::in | File::openmode::binary };
if( !file.is_open() ) {
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) ) {
while( !chThdShouldTerminate() && file.is_open() && (_stats.read_bytes < bytes_to_read) ) {
const halrtcnt_t read_start = halGetCounterValue();
if( !file.read(buffer->data(), buffer->size()) ) {
break;
@ -211,7 +211,7 @@ private:
}
}
file.close();
file.sync();
const halrtcnt_t test_end = halGetCounterValue();
_stats.read_test_duration = test_end - test_start;

View File

@ -24,45 +24,14 @@
#include <string>
#include <algorithm>
#include "bitmap.hpp"
namespace ui {
/* SDCardStatusView *****************************************************/
namespace detail {
static constexpr uint8_t bitmap_sd_card_ok_data[] = {
0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f,
0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f, 0xf8, 0x1f,
0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
};
static constexpr Bitmap bitmap_sd_card_ok {
{ 16, 16 }, bitmap_sd_card_ok_data
};
static constexpr uint8_t bitmap_sd_card_unknown_data[] = {
0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f,
0x38, 0x1c, 0x98, 0x19, 0xf8, 0x19, 0xf8, 0x1c,
0x78, 0x1e, 0x78, 0x1e, 0xf8, 0x1f, 0x78, 0x1e,
0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
};
static constexpr Bitmap bitmap_sd_card_unknown {
{ 16, 16 }, bitmap_sd_card_unknown_data
};
static constexpr uint8_t bitmap_sd_card_error_data[] = {
0x00, 0x00, 0x00, 0x00, 0xe0, 0x1f, 0xf0, 0x1f,
0xf8, 0x1f, 0xd8, 0x1b, 0x98, 0x19, 0x38, 0x1c,
0x78, 0x1e, 0x38, 0x1c, 0x98, 0x19, 0xd8, 0x1b,
0xf8, 0x1f, 0xf8, 0x1f, 0x00, 0x00, 0x00, 0x00,
};
static constexpr Bitmap bitmap_sd_card_error {
{ 16, 16 }, bitmap_sd_card_error_data
};
const Bitmap& bitmap_sd_card(const sd_card::Status status) {
switch(status) {
case sd_card::Status::IOError:
@ -113,7 +82,7 @@ const Color color_sd_card(const sd_card::Status status) {
SDCardStatusView::SDCardStatusView(
const Rect parent_rect
) : Image { parent_rect, &detail::bitmap_sd_card_unknown, detail::color_sd_card_unknown, Color::black() }
) : Image { parent_rect, &bitmap_sd_card_unknown, detail::color_sd_card_unknown, Color::black() }
{
}

View File

@ -114,6 +114,7 @@ void FrequencyScale::draw_frequency_ticks(Painter& painter, const Rect r) {
(magnitude_n >= 6) ? "M" :
(magnitude_n >= 3) ? "k" : "";
const std::string label = to_string_dec_uint(tick_offset) + zero_pad + unit;
const auto label_width = style().font.size_of(label).w;
const Coord offset_low = r.left() + x_center - pixel_offset;
const Rect tick_low { offset_low, r.top(), 1, r.height() };
@ -123,7 +124,7 @@ void FrequencyScale::draw_frequency_ticks(Painter& painter, const Rect r) {
const Coord offset_high = r.left() + x_center + pixel_offset;
const Rect tick_high { offset_high, r.top(), 1, r.height() };
painter.fill_rectangle(tick_high, Color::white());
painter.draw_string({ offset_high + 2, r.top() }, style(), label );
painter.draw_string({ offset_high - 2 - label_width, r.top() }, style(), label );
tick_offset += tick_interval;
}

View File

@ -32,18 +32,12 @@
class StreamInput {
public:
StreamInput(const size_t K) :
StreamInput(const size_t K, CaptureConfig& config) :
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;
config.fifo = &fifo;
}
size_t write(const void* const data, const size_t length) {

View File

@ -96,8 +96,8 @@ void AudioOutput::fill_audio_buffer(const buffer_f32_t& audio, const bool send_t
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]));
if( stream && send_to_fifo ) {
stream->write(audio_int.data(), audio_buffer.count * sizeof(audio_int[0]));
}
feed_audio_stats(audio);

View File

@ -32,6 +32,7 @@
#include "audio_stats_collector.hpp"
#include <cstdint>
#include <memory>
class AudioOutput {
public:
@ -44,6 +45,10 @@ public:
void write(const buffer_s16_t& audio);
void write(const buffer_f32_t& audio);
void set_stream(std::unique_ptr<StreamInput> new_stream) {
stream = std::move(new_stream);
}
private:
static constexpr float k = 32768.0f;
static constexpr float ki = 1.0f / k;
@ -54,7 +59,7 @@ private:
IIRBiquadFilter deemph;
FMSquelch squelch;
StreamInput stream { 14 };
std::unique_ptr<StreamInput> stream;
AudioStatsCollector audio_stats;

View File

@ -63,6 +63,10 @@ void NarrowbandAMAudio::on_message(const Message* const message) {
configure(*reinterpret_cast<const AMConfigureMessage*>(message));
break;
case Message::ID::CaptureConfig:
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
break;
default:
break;
}
@ -93,3 +97,11 @@ void NarrowbandAMAudio::configure(const AMConfigureMessage& message) {
configured = true;
}
void NarrowbandAMAudio::capture_config(const CaptureConfigMessage& message) {
if( message.config ) {
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
} else {
audio_output.set_stream(nullptr);
}
}

View File

@ -72,6 +72,7 @@ private:
bool configured { false };
void configure(const AMConfigureMessage& message);
void capture_config(const CaptureConfigMessage& message);
buffer_f32_t demodulate(const buffer_c16_t& channel);
};

View File

@ -34,23 +34,16 @@ CaptureProcessor::CaptureProcessor() {
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;
channel_filter_pass_f = decim_1_filter.pass_frequency_normalized * decim_1_input_fs;
channel_filter_stop_f = decim_1_filter.stop_frequency_normalized * decim_1_input_fs;
spectrum_interval_samples = channel_filter_output_fs / spectrum_rate_hz;
spectrum_interval_samples = decim_1_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) {
@ -81,7 +74,19 @@ void CaptureProcessor::on_message(const Message* const message) {
channel_spectrum.on_message(message);
break;
case Message::ID::CaptureConfig:
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
break;
default:
break;
}
}
void CaptureProcessor::capture_config(const CaptureConfigMessage& message) {
if( message.config ) {
stream = std::make_unique<StreamInput>(message.config);
} else {
stream.reset();
}
}

View File

@ -41,7 +41,8 @@ public:
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 2457600;
// TODO: Repeated value needs to be transmitted from application side.
static constexpr size_t baseband_fs = 4000000;
static constexpr auto spectrum_rate_hz = 50.0f;
std::array<complex16_t, 512> dst;
@ -60,6 +61,8 @@ private:
SpectrumCollector channel_spectrum;
size_t spectrum_interval_samples = 0;
size_t spectrum_samples = 0;
void capture_config(const CaptureConfigMessage& message);
};
#endif/*__PROC_CAPTURE_HPP__*/

View File

@ -53,6 +53,10 @@ void NarrowbandFMAudio::on_message(const Message* const message) {
configure(*reinterpret_cast<const NBFMConfigureMessage*>(message));
break;
case Message::ID::CaptureConfig:
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
break;
default:
break;
}
@ -81,3 +85,11 @@ void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) {
configured = true;
}
void NarrowbandFMAudio::capture_config(const CaptureConfigMessage& message) {
if( message.config ) {
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
} else {
audio_output.set_stream(nullptr);
}
}

View File

@ -66,6 +66,7 @@ private:
bool configured { false };
void configure(const NBFMConfigureMessage& message);
void capture_config(const CaptureConfigMessage& message);
};
#endif/*__PROC_NFM_AUDIO_H__*/

View File

@ -81,6 +81,10 @@ void WidebandFMAudio::on_message(const Message* const message) {
configure(*reinterpret_cast<const WFMConfigureMessage*>(message));
break;
case Message::ID::CaptureConfig:
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
break;
default:
break;
}
@ -110,3 +114,11 @@ void WidebandFMAudio::configure(const WFMConfigureMessage& message) {
configured = true;
}
void WidebandFMAudio::capture_config(const CaptureConfigMessage& message) {
if( message.config ) {
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
} else {
audio_output.set_stream(nullptr);
}
}

View File

@ -68,6 +68,7 @@ private:
bool configured { false };
void configure(const WFMConfigureMessage& message);
void capture_config(const CaptureConfigMessage& message);
};
#endif/*__PROC_WFM_AUDIO_H__*/

View File

@ -32,18 +32,12 @@
class StreamInput {
public:
StreamInput(const size_t K) :
StreamInput(const size_t K, CaptureConfig& config) :
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;
config.fifo = &fifo;
}
size_t write(const void* const data, const size_t length) {

View File

@ -187,15 +187,15 @@ static void i2c_lld_abort_operation(I2CDriver *i2cp) {
}
static bool_t i2c_lld_tx_not_done(I2CDriver *i2cp) {
return i2cp->txidx < i2cp->txbytes;
return i2cp->txbytes > 0;
}
static bool_t i2c_lld_rx_not_done(I2CDriver *i2cp) {
return i2cp->rxbuf && i2cp->rxbytes;
return i2cp->rxbytes > 0;
}
static bool_t i2c_lld_rx_last_byte(I2CDriver *i2cp) {
return i2cp->rxidx == (i2cp->rxbytes - 1);
return i2cp->rxbytes == 1;
}
/**
@ -249,7 +249,8 @@ static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) {
case I2C_MASTER_TX_DATA_ACK: /* 0x28 */
if (i2c_lld_tx_not_done(i2cp)) {
//i2c_periph_transmit_byte(dp, i2cp->txbuf[i2cp->txidx++]);
dp->DAT = i2cp->txbuf[i2cp->txidx++];
dp->DAT = *i2cp->txbuf++;
i2cp->txbytes--;
dp->CONCLR = I2C_CONCLR_SIC;
} else {
if (i2c_lld_rx_not_done(i2cp)) {
@ -266,7 +267,8 @@ static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) {
break;
case I2C_MASTER_RX_DATA_ACK: /* 0x50 */
i2cp->rxbuf[i2cp->rxidx++] = i2c_periph_read_byte(dp);
*i2cp->rxbuf++ = i2c_periph_read_byte(dp);
i2cp->rxbytes--;
/* fall through */
case I2C_MASTER_RX_ADDR_ACK: /* 0x40 */
if (i2c_lld_rx_last_byte(i2cp)) {
@ -277,7 +279,8 @@ static void i2c_lld_serve_event_interrupt(I2CDriver *i2cp) {
break;
case I2C_MASTER_RX_DATA_NACK: /* 0x58 */
i2cp->rxbuf[i2cp->rxidx] = i2c_periph_read_byte(dp);
*i2cp->rxbuf++ = i2c_periph_read_byte(dp);
i2cp->rxbytes--;
i2c_periph_stop(dp);
wakeup_isr(i2cp, RDY_OK);
/* fall through */
@ -474,10 +477,8 @@ static msg_t i2c_lld_master_start(I2CDriver *i2cp, uint_fast8_t addr_r,
i2cp->addr_r = addr_r;
i2cp->txbuf = txbuf;
i2cp->txbytes = txbytes;
i2cp->txidx = 0;
i2cp->rxbuf = rxbuf;
i2cp->rxbytes = rxbytes;
i2cp->rxidx = 0;
/* Atomic check on the timer in order to make sure that a timeout didn't
happen outside the critical zone.*/

View File

@ -169,10 +169,6 @@ struct I2CDriver {
* @brief Number of bytes of data to send.
*/
size_t txbytes;
/**
* @brief Current index in buffer when sending data.
*/
size_t txidx;
/**
* @brief Pointer to the buffer to put received data.
*/
@ -181,10 +177,6 @@ struct I2CDriver {
* @brief Number of bytes of data to receive.
*/
size_t rxbytes;
/**
* @brief Current index in buffer when receiving data.
*/
size_t rxidx;
/**
* @brief Pointer to the I2Cx registers block.
*/

View File

@ -580,6 +580,11 @@ void sdc_lld_start(SDCDriver *sdcp) {
sdio_reset();
sdio_reset_card();
// UM10503 recommendation
LPC_SCU->SDDELAY =
(0x8 << 0)
| (0xf << 8)
;
LPC_SDMMC->CTRL =
(1U << 4) /* INT_ENABLE */
| (1U << 25) /* USE_INTERNAL_DMAC */

View File

@ -1,146 +0,0 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
Copyright (C) 2014 Jared Boone, ShareBrained Technology
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
* LPC43xx M4 memory setup.
*/
__main_stack_size__ = 0x1000; /* Exceptions/interrupts stack */
__process_stack_size__ = 0x6000; /* main() stack */
MEMORY
{
flash : org = 0x00000000, len = 512k /* Flash bank A @ 0x1a000000 */
ram : org = 0x10080000, len = 40k /* Local SRAM @ 0x10080000 */
}
__ram_start__ = ORIGIN(ram);
__ram_size__ = LENGTH(ram);
__ram_end__ = __ram_start__ + __ram_size__;
ENTRY(ResetHandler)
SECTIONS
{
. = 0;
_text = .;
startup : ALIGN(16) SUBALIGN(16)
{
KEEP(*(vectors))
} > flash
constructors : ALIGN(4) SUBALIGN(4)
{
PROVIDE(__init_array_start = .);
KEEP(*(SORT(.init_array.*)))
KEEP(*(.init_array))
PROVIDE(__init_array_end = .);
} > flash
destructors : ALIGN(4) SUBALIGN(4)
{
PROVIDE(__fini_array_start = .);
KEEP(*(.fini_array))
KEEP(*(SORT(.fini_array.*)))
PROVIDE(__fini_array_end = .);
} > flash
.text : ALIGN(16) SUBALIGN(16)
{
*(.text.startup.*)
*(.text)
*(.text.*)
*(.rodata)
*(.rodata.*)
*(.glue_7t)
*(.glue_7)
*(.gcc*)
} > flash
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > flash
.ARM.exidx : {
PROVIDE(__exidx_start = .);
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
PROVIDE(__exidx_end = .);
} > flash
.eh_frame_hdr :
{
*(.eh_frame_hdr)
} > flash
.eh_frame : ONLY_IF_RO
{
*(.eh_frame)
} > flash
.textalign : ONLY_IF_RO
{
. = ALIGN(8);
} > flash
. = ALIGN(4);
_etext = .;
_textdata = _etext;
.stacks :
{
. = ALIGN(8);
__main_stack_base__ = .;
. += __main_stack_size__;
. = ALIGN(8);
__main_stack_end__ = .;
__process_stack_base__ = .;
__main_thread_stack_base__ = .;
. += __process_stack_size__;
. = ALIGN(8);
__process_stack_end__ = .;
__main_thread_stack_end__ = .;
} > ram
.data ALIGN(4) : ALIGN(4)
{
. = ALIGN(4);
PROVIDE(_data = .);
*(.data)
*(.data.*)
*(.ramtext)
. = ALIGN(4);
PROVIDE(_edata = .);
} > ram AT > flash
.bss ALIGN(4) : ALIGN(4)
{
. = ALIGN(4);
PROVIDE(_bss_start = .);
*(.bss)
*(.bss.*)
*(COMMON)
. = ALIGN(4);
PROVIDE(_bss_end = .);
} > ram
}
PROVIDE(end = .);
_end = .;
__heap_base__ = _end;
__heap_end__ = __ram_end__;

View File

@ -26,6 +26,7 @@
#include <cstddef>
#include <array>
#include <functional>
#include <algorithm>
#include "baseband_packet.hpp"
#include "ert_packet.hpp"
@ -61,13 +62,15 @@ public:
ChannelSpectrumConfig = 14,
SpectrumStreamingConfig = 15,
DisplaySleep = 16,
TXDone = 17,
Retune = 18,
ReadyForSwitch = 19,
AFSKData = 20,
ModuleID = 21,
FIFOSignal = 22,
FIFOData = 23,
CaptureConfig = 17,
TXDone = 20,
Retune = 21,
ReadyForSwitch = 22,
AFSKData = 23,
ModuleID = 24,
FIFOSignal = 25,
FIFOData = 26,
MAX
};
@ -416,6 +419,46 @@ public:
const iir_biquad_config_t audio_hpf_config;
};
struct CaptureConfig {
const size_t write_size_log2;
const size_t buffer_count_log2;
uint64_t baseband_bytes_received;
uint64_t baseband_bytes_dropped;
FIFO<uint8_t>* fifo;
constexpr CaptureConfig(
const size_t write_size_log2,
const size_t buffer_count_log2
) : write_size_log2 { write_size_log2 },
buffer_count_log2 { buffer_count_log2 },
baseband_bytes_received { 0 },
baseband_bytes_dropped { 0 },
fifo { nullptr }
{
}
size_t dropped_percent() const {
if( baseband_bytes_dropped == 0 ) {
return 0;
} else {
const size_t percent = baseband_bytes_dropped * 100U / baseband_bytes_received;
return std::max(1U, percent);
}
}
};
class CaptureConfigMessage : public Message {
public:
constexpr CaptureConfigMessage(
CaptureConfig* const config
) : Message { ID::CaptureConfig },
config { config }
{
}
CaptureConfig* const config;
};
class TXDoneMessage : public Message {
public:
TXDoneMessage(

View File

@ -20,3 +20,18 @@
*/
#include "message_queue.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
#if defined(LPC43XX_M0)
void MessageQueue::signal() {
creg::m0apptxevent::assert();
}
#endif
#if defined(LPC43XX_M4)
void MessageQueue::signal() {
creg::m4txevent::assert();
}
#endif

View File

@ -27,9 +27,6 @@
#include "message.hpp"
#include "fifo.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
#include <ch.h>
class MessageQueue {
@ -73,6 +70,10 @@ public:
}
}
bool is_empty() const {
return fifo.is_empty();
}
private:
FIFO<uint8_t> fifo;
Mutex mutex_write;
@ -95,10 +96,6 @@ private:
return fifo.len();
}
bool is_empty() const {
return fifo.is_empty();
}
bool push(const void* const buf, const size_t len) {
chMtxLock(&mutex_write);
const auto result = fifo.in_r(buf, len);
@ -111,18 +108,7 @@ private:
return success;
}
#if defined(LPC43XX_M0)
void signal() {
creg::m0apptxevent::assert();
}
#endif
#if defined(LPC43XX_M4)
void signal() {
creg::m4txevent::assert();
}
#endif
void signal();
};
#endif/*__MESSAGE_QUEUE_H__*/

View File

@ -51,9 +51,8 @@ static constexpr std::array<uint8_t, 12> png_iend { {
PNGWriter::PNGWriter(
const std::string& filename
)
) : file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc }
{
file.open_for_writing(filename);
file.write(png_file_header);
file.write(png_ihdr_screen_capture);

View File

@ -47,7 +47,6 @@ struct SharedMemory {
uint8_t baseband_queue_data[1 << baseband_queue_k];
MessageQueue application_queue;
uint8_t application_queue_data[1 << application_queue_k];
void* FIFO_HACK;
// TODO: M0 should directly configure and control DMA channel that is
// acquiring ADC samples.

View File

@ -85,67 +85,78 @@ static void widget_collect_visible(Widget* const w, TestFn test, test_collection
}
}
static int32_t rect_distances(
const KeyEvent direction,
const Rect& rect_start,
const Rect& rect_end
) {
Coord on_axis_max, on_axis_min;
switch(direction) {
case KeyEvent::Right:
on_axis_max = rect_end.left();
on_axis_min = rect_start.right();
break;
case KeyEvent::Left:
on_axis_max = rect_start.left();
on_axis_min = rect_end.right();
break;
case KeyEvent::Down:
on_axis_max = rect_end.top();
on_axis_min = rect_start.bottom();
break;
case KeyEvent::Up:
on_axis_max = rect_start.top();
on_axis_min = rect_end.bottom();
break;
default:
return -1;
}
Coord on_axis_distance = on_axis_max - on_axis_min;
if( on_axis_distance < 0 ) {
return -1;
}
Coord perpendicular_axis_start, perpendicular_axis_end;
switch(direction) {
case KeyEvent::Right:
case KeyEvent::Left:
perpendicular_axis_start = rect_start.center().y;
perpendicular_axis_end = rect_end.center().y;
break;
case KeyEvent::Up:
case KeyEvent::Down:
perpendicular_axis_start = rect_start.center().x;
perpendicular_axis_end = rect_end.center().x;
break;
default:
return -1;
}
return (std::abs(perpendicular_axis_end - perpendicular_axis_start) + 1) * (on_axis_distance + 1);
}
void FocusManager::update(
Widget* const top_widget,
const KeyEvent event
) {
if( focus_widget() ) {
const auto focus_screen_rect = focus_widget()->screen_rect();
const auto center = focus_screen_rect.center();
const auto test_fn = [&center, event](ui::Widget* const w) -> test_result_t {
const auto test_fn = [&focus_screen_rect, event](ui::Widget* const w) -> test_result_t {
// if( w->visible() && w->focusable() ) {
if( w->focusable() ) {
const Point delta = w->screen_rect().center() - center;
/* Heuristic to compute closeness. */
/* TODO: Look at metric involving overlap of current
* widget rectangle in the direction of movement, with
* all the prospective widgets.
*/
switch(event) {
case KeyEvent::Right:
if( delta.x > 0 ) {
if( delta.y == 0 ) {
return { w, delta.x };
} else {
return { w, delta.x * abs(delta.y) + 1000 };
}
}
break;
case KeyEvent::Left:
if( delta.x < 0 ) {
if( delta.y == 0 ) {
return { w, -delta.x };
} else {
return { w, -delta.x * abs(delta.y) + 1000 };
}
}
break;
case KeyEvent::Down:
if( delta.y > 0 ) {
if( delta.x == 0 ) {
return { w, delta.y };
} else {
return { w, delta.y * abs(delta.x) + 1000 };
}
}
break;
case KeyEvent::Up:
if( delta.y < 0 ) {
if( delta.x == 0 ) {
return { w, -delta.y };
} else {
return { w, -delta.y * abs(delta.x) + 1000 };
}
}
break;
default:
break;
const auto distance = rect_distances(event, focus_screen_rect, w->screen_rect());
if( distance >= 0 ) {
return { w, distance };
}
}
@ -167,34 +178,5 @@ void FocusManager::update(
}
}
}
#if 0
void FocusManager::update(
Widget* const top_widget,
const TouchEvent event
) {
const auto test_fn = [event](Widget* const w) -> test_result_t {
if( w->focusable() ) {
const auto r = w->screen_rect();
if( r.contains(event) ) {
return { w, 0 };
}
}
return { nullptr, { } };
};
test_collection_t collection;
widget_collect_visible(
top_widget, test_fn,
collection
);
if( !collection.empty() ) {
// Take the last object in the collection, it will be rendered last,
// therefore appear "on top".
const auto touched = collection.back().first;
touched->on_touch(event);
}
}
#endif
} /* namespace ui */

View File

@ -38,7 +38,7 @@ Size Widget::size() const {
return parent_rect.size;
}
Rect Widget::screen_rect() {
Rect Widget::screen_rect() const {
return parent() ? (parent_rect + parent()->screen_pos()) : parent_rect;
}

View File

@ -51,12 +51,12 @@ private:
class Widget {
public:
constexpr Widget(
Widget(
) : parent_rect { }
{
}
constexpr Widget(
Widget(
Rect parent_rect
) : parent_rect { parent_rect }
{
@ -69,7 +69,7 @@ public:
Point screen_pos();
Size size() const;
Rect screen_rect();
Rect screen_rect() const;
virtual void set_parent_rect(const Rect new_parent_rect);
Widget* parent() const;