mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-12-24 06:49:24 -05:00
Merge
This commit is contained in:
parent
8523bd860e
commit
569f299f42
@ -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++"
|
||||
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
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));
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// 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 ) {
|
||||
const auto filename = next_filename_matching_pattern("AUD_????.S16");
|
||||
if( !filename.empty() ) {
|
||||
audio_thread = std::make_unique<AudioThread>(filename);
|
||||
}
|
||||
audio::output::unmute();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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__*/
|
156
firmware/application/bitmap.hpp
Normal file
156
firmware/application/bitmap.hpp
Normal 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__*/
|
@ -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) {
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
122
firmware/application/capture_thread.cpp
Normal file
122
firmware/application/capture_thread.cpp
Normal 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;
|
||||
}
|
67
firmware/application/capture_thread.hpp
Normal file
67
firmware/application/capture_thread.hpp
Normal 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__*/
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool File::close() {
|
||||
File::~File() {
|
||||
f_close(&f);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool File::is_ready() {
|
||||
return f_error(&f) == 0;
|
||||
}
|
||||
|
||||
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 {
|
||||
|
@ -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 {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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__*/
|
||||
|
@ -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();
|
||||
|
@ -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 */
|
35
firmware/application/time.hpp
Normal file
35
firmware/application/time.hpp
Normal 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__*/
|
@ -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);
|
||||
|
||||
|
@ -34,7 +34,7 @@ namespace ui {
|
||||
|
||||
class Audio : public Widget {
|
||||
public:
|
||||
constexpr Audio(
|
||||
Audio(
|
||||
const Rect parent_rect
|
||||
) : Widget { parent_rect },
|
||||
rms_db_ { -120 },
|
||||
|
@ -34,7 +34,7 @@ namespace ui {
|
||||
|
||||
class Channel : public Widget {
|
||||
public:
|
||||
constexpr Channel(
|
||||
Channel(
|
||||
const Rect parent_rect
|
||||
) : Widget { parent_rect },
|
||||
max_db_ { -120 }
|
||||
|
@ -84,7 +84,7 @@ private:
|
||||
|
||||
class TemperatureWidget : public Widget {
|
||||
public:
|
||||
explicit constexpr TemperatureWidget(
|
||||
explicit TemperatureWidget(
|
||||
Rect parent_rect
|
||||
) : Widget { parent_rect }
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
251
firmware/application/ui_record_view.cpp
Normal file
251
firmware/application/ui_record_view.cpp
Normal 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 */
|
103
firmware/application/ui_record_view.hpp
Normal file
103
firmware/application/ui_record_view.hpp
Normal 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__*/
|
@ -34,7 +34,7 @@ namespace ui {
|
||||
|
||||
class RSSI : public Widget {
|
||||
public:
|
||||
constexpr RSSI(
|
||||
RSSI(
|
||||
const Rect parent_rect
|
||||
) : Widget { parent_rect },
|
||||
min_ { 0 },
|
||||
|
@ -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;
|
||||
|
@ -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() }
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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__*/
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ private:
|
||||
|
||||
bool configured { false };
|
||||
void configure(const NBFMConfigureMessage& message);
|
||||
void capture_config(const CaptureConfigMessage& message);
|
||||
};
|
||||
|
||||
#endif/*__PROC_NFM_AUDIO_H__*/
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,7 @@ private:
|
||||
|
||||
bool configured { false };
|
||||
void configure(const WFMConfigureMessage& message);
|
||||
void capture_config(const CaptureConfigMessage& message);
|
||||
};
|
||||
|
||||
#endif/*__PROC_WFM_AUDIO_H__*/
|
||||
|
@ -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) {
|
||||
|
@ -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.*/
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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 */
|
||||
|
@ -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__;
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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__*/
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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 = [¢er, 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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user