mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-01-11 07:19:34 -05:00
Commit replay stuff before sync
This commit is contained in:
parent
3ec725c172
commit
12aeae3a82
@ -172,6 +172,7 @@ set(CPPSRC
|
||||
ui_rds.cpp
|
||||
ui_receiver.cpp
|
||||
ui_record_view.cpp
|
||||
ui_replay_view.cpp
|
||||
ui_rssi.cpp
|
||||
ui_sd_card_status_view.cpp
|
||||
# ui_sd_card_debug.cpp
|
||||
@ -198,6 +199,7 @@ set(CPPSRC
|
||||
ert_app.cpp
|
||||
${COMMON}/ert_packet.cpp
|
||||
capture_app.cpp
|
||||
replay_app.cpp
|
||||
sd_card.cpp
|
||||
time.cpp
|
||||
file.cpp
|
||||
@ -206,6 +208,7 @@ set(CPPSRC
|
||||
log_file.cpp
|
||||
${COMMON}/png_writer.cpp
|
||||
capture_thread.cpp
|
||||
replay_thread.cpp
|
||||
${COMMON}/manchester.cpp
|
||||
string_format.cpp
|
||||
temperature_logger.cpp
|
||||
|
@ -3731,6 +3731,60 @@ recent_entries.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/recent_entries.cpp.s
|
||||
.PHONY : recent_entries.cpp.s
|
||||
|
||||
replay_app.obj: replay_app.cpp.obj
|
||||
|
||||
.PHONY : replay_app.obj
|
||||
|
||||
# target to build an object file
|
||||
replay_app.cpp.obj:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/replay_app.cpp.obj
|
||||
.PHONY : replay_app.cpp.obj
|
||||
|
||||
replay_app.i: replay_app.cpp.i
|
||||
|
||||
.PHONY : replay_app.i
|
||||
|
||||
# target to preprocess a source file
|
||||
replay_app.cpp.i:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/replay_app.cpp.i
|
||||
.PHONY : replay_app.cpp.i
|
||||
|
||||
replay_app.s: replay_app.cpp.s
|
||||
|
||||
.PHONY : replay_app.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
replay_app.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/replay_app.cpp.s
|
||||
.PHONY : replay_app.cpp.s
|
||||
|
||||
replay_thread.obj: replay_thread.cpp.obj
|
||||
|
||||
.PHONY : replay_thread.obj
|
||||
|
||||
# target to build an object file
|
||||
replay_thread.cpp.obj:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/replay_thread.cpp.obj
|
||||
.PHONY : replay_thread.cpp.obj
|
||||
|
||||
replay_thread.i: replay_thread.cpp.i
|
||||
|
||||
.PHONY : replay_thread.i
|
||||
|
||||
# target to preprocess a source file
|
||||
replay_thread.cpp.i:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/replay_thread.cpp.i
|
||||
.PHONY : replay_thread.cpp.i
|
||||
|
||||
replay_thread.s: replay_thread.cpp.s
|
||||
|
||||
.PHONY : replay_thread.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
replay_thread.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/replay_thread.cpp.s
|
||||
.PHONY : replay_thread.cpp.s
|
||||
|
||||
rf_path.obj: rf_path.cpp.obj
|
||||
|
||||
.PHONY : rf_path.obj
|
||||
@ -4784,6 +4838,33 @@ ui_record_view.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_record_view.cpp.s
|
||||
.PHONY : ui_record_view.cpp.s
|
||||
|
||||
ui_replay_view.obj: ui_replay_view.cpp.obj
|
||||
|
||||
.PHONY : ui_replay_view.obj
|
||||
|
||||
# target to build an object file
|
||||
ui_replay_view.cpp.obj:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_replay_view.cpp.obj
|
||||
.PHONY : ui_replay_view.cpp.obj
|
||||
|
||||
ui_replay_view.i: ui_replay_view.cpp.i
|
||||
|
||||
.PHONY : ui_replay_view.i
|
||||
|
||||
# target to preprocess a source file
|
||||
ui_replay_view.cpp.i:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_replay_view.cpp.i
|
||||
.PHONY : ui_replay_view.cpp.i
|
||||
|
||||
ui_replay_view.s: ui_replay_view.cpp.s
|
||||
|
||||
.PHONY : ui_replay_view.s
|
||||
|
||||
# target to generate assembly for a file
|
||||
ui_replay_view.cpp.s:
|
||||
cd /home/furrtek/portapack-hackrf && $(MAKE) -f firmware/application/CMakeFiles/application.elf.dir/build.make firmware/application/CMakeFiles/application.elf.dir/ui_replay_view.cpp.s
|
||||
.PHONY : ui_replay_view.cpp.s
|
||||
|
||||
ui_rssi.obj: ui_rssi.cpp.obj
|
||||
|
||||
.PHONY : ui_rssi.obj
|
||||
@ -5463,6 +5544,12 @@ help:
|
||||
@echo "... recent_entries.obj"
|
||||
@echo "... recent_entries.i"
|
||||
@echo "... recent_entries.s"
|
||||
@echo "... replay_app.obj"
|
||||
@echo "... replay_app.i"
|
||||
@echo "... replay_app.s"
|
||||
@echo "... replay_thread.obj"
|
||||
@echo "... replay_thread.i"
|
||||
@echo "... replay_thread.s"
|
||||
@echo "... rf_path.obj"
|
||||
@echo "... rf_path.i"
|
||||
@echo "... rf_path.s"
|
||||
@ -5580,6 +5667,9 @@ help:
|
||||
@echo "... ui_record_view.obj"
|
||||
@echo "... ui_record_view.i"
|
||||
@echo "... ui_record_view.s"
|
||||
@echo "... ui_replay_view.obj"
|
||||
@echo "... ui_replay_view.i"
|
||||
@echo "... ui_replay_view.s"
|
||||
@echo "... ui_rssi.obj"
|
||||
@echo "... ui_rssi.i"
|
||||
@echo "... ui_rssi.s"
|
||||
|
@ -512,6 +512,29 @@ static constexpr Bitmap bitmap_icon_remote {
|
||||
{ 16, 16 }, bitmap_icon_remote_data
|
||||
};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_replay_data[] = {
|
||||
0x00, 0x00,
|
||||
0xC0, 0x07,
|
||||
0xF0, 0x1F,
|
||||
0x79, 0x3C,
|
||||
0x1D, 0x70,
|
||||
0x0F, 0x60,
|
||||
0x07, 0xE0,
|
||||
0x1F, 0xC0,
|
||||
0x00, 0xC0,
|
||||
0x00, 0xE0,
|
||||
0x00, 0x60,
|
||||
0x00, 0x70,
|
||||
0x30, 0x3C,
|
||||
0xE0, 0x0F,
|
||||
0x80, 0x03,
|
||||
0x00, 0x00,
|
||||
};
|
||||
|
||||
static constexpr Bitmap bitmap_icon_replay {
|
||||
{ 16, 16 }, bitmap_icon_replay_data
|
||||
};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_soundboard_data[] = {
|
||||
0x00, 0x00,
|
||||
0xDE, 0x7B,
|
||||
|
@ -26,8 +26,8 @@
|
||||
//TEST: Imperial in whipcalc
|
||||
//TEST: Numbers
|
||||
//TEST: Jammer
|
||||
//TODO: Frequency manager auto-remove duplicates
|
||||
|
||||
//TODO: Frequency manager auto-remove duplicates
|
||||
//TODO: "TX box" view or composite widget with frequency and bw settings, simple and advanced setup TX buttons...
|
||||
//TODO: Morse coder for foxhunts
|
||||
//TODO: Finish EPAR tx
|
||||
|
121
firmware/application/replay_app.cpp
Normal file
121
firmware/application/replay_app.cpp
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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 "replay_app.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
ReplayAppView::ReplayAppView(NavigationView& nav) {
|
||||
baseband::run_image(portapack::spi_flash::image_tag_replay);
|
||||
|
||||
add_children({ {
|
||||
&channel,
|
||||
&field_frequency,
|
||||
&field_frequency_step,
|
||||
&field_rf_amp,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&replay_view,
|
||||
&waterfall,
|
||||
} });
|
||||
|
||||
field_frequency.set_value(target_frequency());
|
||||
field_frequency.set_step(receiver_model.frequency_step());
|
||||
field_frequency.on_change = [this](rf::Frequency f) {
|
||||
this->on_target_frequency_changed(f);
|
||||
};
|
||||
field_frequency.on_edit = [this, &nav]() {
|
||||
// TODO: Provide separate modal method/scheme?
|
||||
auto new_view = nav.push<FrequencyKeypadView>(this->target_frequency());
|
||||
new_view->on_changed = [this](rf::Frequency f) {
|
||||
this->on_target_frequency_changed(f);
|
||||
this->field_frequency.set_value(f);
|
||||
};
|
||||
};
|
||||
|
||||
field_frequency_step.set_by_value(receiver_model.frequency_step());
|
||||
field_frequency_step.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
receiver_model.set_frequency_step(v);
|
||||
this->field_frequency.set_step(v);
|
||||
};
|
||||
|
||||
radio::enable({
|
||||
target_frequency(),
|
||||
sampling_rate,
|
||||
baseband_bandwidth,
|
||||
rf::Direction::Transmit,
|
||||
receiver_model.rf_amp(),
|
||||
static_cast<int8_t>(receiver_model.lna()),
|
||||
static_cast<int8_t>(receiver_model.vga()),
|
||||
1,
|
||||
});
|
||||
|
||||
replay_view.set_sampling_rate(sampling_rate / 8);
|
||||
replay_view.on_error = [&nav](std::string message) {
|
||||
nav.display_modal("Error", message);
|
||||
};
|
||||
}
|
||||
|
||||
ReplayAppView::~ReplayAppView() {
|
||||
radio::disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void ReplayAppView::on_hide() {
|
||||
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
|
||||
// it's being shown or hidden.
|
||||
waterfall.on_hide();
|
||||
View::on_hide();
|
||||
}
|
||||
|
||||
void ReplayAppView::set_parent_rect(const Rect new_parent_rect) {
|
||||
View::set_parent_rect(new_parent_rect);
|
||||
|
||||
const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), static_cast<ui::Dim>(new_parent_rect.height() - header_height) };
|
||||
waterfall.set_parent_rect(waterfall_rect);
|
||||
}
|
||||
|
||||
void ReplayAppView::focus() {
|
||||
field_frequency.focus();
|
||||
}
|
||||
|
||||
void ReplayAppView::on_target_frequency_changed(rf::Frequency f) {
|
||||
set_target_frequency(f);
|
||||
}
|
||||
|
||||
void ReplayAppView::set_target_frequency(const rf::Frequency new_value) {
|
||||
persistent_memory::set_tuned_frequency(new_value);;
|
||||
}
|
||||
|
||||
rf::Frequency ReplayAppView::target_frequency() const {
|
||||
return persistent_memory::tuned_frequency();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
95
firmware/application/replay_app.hpp
Normal file
95
firmware/application/replay_app.hpp
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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 __REPLAY_APP_HPP__
|
||||
#define __REPLAY_APP_HPP__
|
||||
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "ui_replay_view.hpp"
|
||||
#include "ui_spectrum.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class ReplayAppView : public View {
|
||||
public:
|
||||
ReplayAppView(NavigationView& nav);
|
||||
~ReplayAppView();
|
||||
|
||||
void on_hide() override;
|
||||
|
||||
void set_parent_rect(const Rect new_parent_rect) override;
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override { return "Capture"; };
|
||||
|
||||
private:
|
||||
static constexpr ui::Dim header_height = 2 * 16;
|
||||
|
||||
static constexpr uint32_t sampling_rate = 4000000;
|
||||
static constexpr uint32_t baseband_bandwidth = 2500000;
|
||||
|
||||
void on_target_frequency_changed(rf::Frequency f);
|
||||
|
||||
rf::Frequency target_frequency() const;
|
||||
void set_target_frequency(const rf::Frequency new_value);
|
||||
|
||||
Channel channel {
|
||||
{ 24 * 8, 5, 6 * 8, 4 },
|
||||
};
|
||||
|
||||
FrequencyField field_frequency {
|
||||
{ 0 * 8, 0 * 16 },
|
||||
};
|
||||
|
||||
FrequencyStepView field_frequency_step {
|
||||
{ 10 * 8, 0 * 16 },
|
||||
};
|
||||
|
||||
RFAmpField field_rf_amp {
|
||||
{ 16 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
LNAGainField field_lna {
|
||||
{ 18 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
VGAGainField field_vga {
|
||||
{ 21 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
ReplayView replay_view {
|
||||
{ 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
|
||||
"BBD_????", ReplayView::FileType::RawS16, 16384, 3
|
||||
};
|
||||
|
||||
spectrum::WaterfallWidget waterfall;
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__REPLAY_APP_HPP__*/
|
143
firmware/application/replay_thread.cpp
Normal file
143
firmware/application/replay_thread.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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 "replay_thread.hpp"
|
||||
|
||||
#include "baseband_api.hpp"
|
||||
|
||||
// StreamOutput ///////////////////////////////////////////////////////////
|
||||
|
||||
class StreamOutput {
|
||||
public:
|
||||
StreamOutput(CaptureConfig* const config);
|
||||
~StreamOutput();
|
||||
|
||||
size_t available() {
|
||||
return fifo_buffers_full->len();
|
||||
}
|
||||
|
||||
StreamBuffer* get_buffer() {
|
||||
StreamBuffer* p { nullptr };
|
||||
fifo_buffers_full->out(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
bool release_buffer(StreamBuffer* const p) {
|
||||
p->empty();
|
||||
return fifo_buffers_empty->in(p);
|
||||
}
|
||||
|
||||
static FIFO<StreamBuffer*>* fifo_buffers_empty;
|
||||
static FIFO<StreamBuffer*>* fifo_buffers_full;
|
||||
|
||||
private:
|
||||
CaptureConfig* const config;
|
||||
};
|
||||
|
||||
FIFO<StreamBuffer*>* StreamOutput::fifo_buffers_empty = nullptr;
|
||||
FIFO<StreamBuffer*>* StreamOutput::fifo_buffers_full = nullptr;
|
||||
|
||||
StreamOutput::StreamOutput(
|
||||
CaptureConfig* const config
|
||||
) : config { config }
|
||||
{
|
||||
baseband::capture_start(config);
|
||||
fifo_buffers_empty = config->fifo_buffers_empty;
|
||||
fifo_buffers_full = config->fifo_buffers_full;
|
||||
}
|
||||
|
||||
StreamOutput::~StreamOutput() {
|
||||
fifo_buffers_full = nullptr;
|
||||
fifo_buffers_empty = nullptr;
|
||||
baseband::capture_stop();
|
||||
}
|
||||
|
||||
// CaptureThread //////////////////////////////////////////////////////////
|
||||
|
||||
Thread* ReplayThread::thread = nullptr;
|
||||
|
||||
ReplayThread::ReplayThread(
|
||||
std::unique_ptr<Reader> reader,
|
||||
size_t read_size,
|
||||
size_t buffer_count,
|
||||
std::function<void()> success_callback,
|
||||
std::function<void(File::Error)> error_callback
|
||||
) : config { read_size, buffer_count },
|
||||
reader { std::move(reader) },
|
||||
success_callback { std::move(success_callback) },
|
||||
error_callback { std::move(error_callback) }
|
||||
{
|
||||
// Need significant stack for FATFS
|
||||
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, ReplayThread::static_fn, this);
|
||||
}
|
||||
|
||||
ReplayThread::~ReplayThread() {
|
||||
if( thread ) {
|
||||
chThdTerminate(thread);
|
||||
chEvtSignal(thread, event_mask_loop_wake);
|
||||
chThdWait(thread);
|
||||
thread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ReplayThread::check_fifo_isr() {
|
||||
// TODO: Prevent over-signalling by transmitting a set of
|
||||
// flags from the baseband core.
|
||||
const auto fifo = StreamOutput::fifo_buffers_full;
|
||||
if( fifo ) {
|
||||
if( !fifo->is_empty() ) {
|
||||
chEvtSignalI(thread, event_mask_loop_wake);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msg_t ReplayThread::static_fn(void* arg) {
|
||||
auto obj = static_cast<ReplayThread*>(arg);
|
||||
const auto error = obj->run();
|
||||
if( error.is_valid() && obj->error_callback ) {
|
||||
obj->error_callback(error.value());
|
||||
} else {
|
||||
if( obj->success_callback ) {
|
||||
obj->success_callback();
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Optional<File::Error> ReplayThread::run() {
|
||||
StreamOutput stream { &config };
|
||||
|
||||
while( !chThdShouldTerminate() ) {
|
||||
if( stream.available() ) {
|
||||
auto buffer = stream.get_buffer();
|
||||
auto read_result = reader->reader(buffer->data(), buffer->size());
|
||||
if( read_result.is_error() ) {
|
||||
return read_result.error();
|
||||
}
|
||||
stream.release_buffer(buffer);
|
||||
} else {
|
||||
chEvtWaitAny(event_mask_loop_wake);
|
||||
}
|
||||
}
|
||||
|
||||
return { };
|
||||
}
|
74
firmware/application/replay_thread.hpp
Normal file
74
firmware/application/replay_thread.hpp
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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 __REPLAY_THREAD_H__
|
||||
#define __REPLAY_THREAD_H__
|
||||
|
||||
#include "ch.h"
|
||||
|
||||
#include "event_m0.hpp"
|
||||
|
||||
#include "file.hpp"
|
||||
#include "optional.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
class Reader {
|
||||
public:
|
||||
virtual File::Result<size_t> read(const void* const buffer, const size_t bytes) = 0;
|
||||
virtual ~Reader() = default;
|
||||
};
|
||||
|
||||
class ReplayThread {
|
||||
public:
|
||||
ReplayThread(
|
||||
std::unique_ptr<Reader> reader,
|
||||
size_t read_size,
|
||||
size_t buffer_count,
|
||||
std::function<void()> success_callback,
|
||||
std::function<void(File::Error)> error_callback
|
||||
);
|
||||
~ReplayThread();
|
||||
|
||||
const ReplayConfig& state() const {
|
||||
return config;
|
||||
}
|
||||
|
||||
static void check_fifo_isr();
|
||||
|
||||
private:
|
||||
static constexpr auto event_mask_loop_wake = EVENT_MASK(0);
|
||||
|
||||
ReplayConfig config;
|
||||
std::unique_ptr<Reader> reader;
|
||||
std::function<void()> success_callback;
|
||||
std::function<void(File::Error)> error_callback;
|
||||
static Thread* thread;
|
||||
|
||||
static msg_t static_fn(void* arg);
|
||||
|
||||
Optional<File::Error> run();
|
||||
};
|
||||
|
||||
#endif/*__REPLAY_THREAD_H__*/
|
@ -55,6 +55,7 @@
|
||||
#include "tpms_app.hpp"
|
||||
#include "pocsag_app.hpp"
|
||||
#include "capture_app.hpp"
|
||||
#include "replay_app.hpp"
|
||||
|
||||
#include "core_control.hpp"
|
||||
|
||||
@ -338,16 +339,16 @@ void SystemMenuView::hackrf_mode(NavigationView& nav) {
|
||||
}
|
||||
|
||||
SystemMenuView::SystemMenuView(NavigationView& nav) {
|
||||
add_items<11>({ {
|
||||
add_items<12>({ {
|
||||
{ "Play dead", ui::Color::red(), &bitmap_icon_playdead, [&nav](){ nav.push<PlayDeadView>(); } },
|
||||
{ "Receivers", ui::Color::cyan(), &bitmap_icon_receiver, [&nav](){ nav.push<ReceiverMenuView>(); } },
|
||||
{ "Capture", ui::Color::cyan(), &bitmap_icon_capture, [&nav](){ nav.push<CaptureAppView>(); } },
|
||||
{ "Replay", ui::Color::blue(), &bitmap_icon_replay, [&nav](){ nav.push<ReplayAppView>(); } },
|
||||
{ "Code transmitters", ui::Color::green(), &bitmap_icon_codetx, [&nav](){ nav.push<TransmitterCodedMenuView>(); } },
|
||||
{ "Audio transmitters", ui::Color::green(), &bitmap_icon_audiotx, [&nav](){ nav.push<TransmitterAudioMenuView>(); } },
|
||||
{ "Close Call", ui::Color::orange(),&bitmap_icon_closecall, [&nav](){ nav.push<CloseCallView>(); } },
|
||||
{ "Jammer", ui::Color::orange(),&bitmap_icon_jammer, [&nav](){ nav.push<JammerView>(); } },
|
||||
{ "Utilities", ui::Color::purple(),nullptr, [&nav](){ nav.push<UtilitiesView>(); } },
|
||||
//{ "Analyze", ui::Color::white(), [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "Setup", ui::Color::white(), nullptr, [&nav](){ nav.push<SetupMenuView>(); } },
|
||||
//{ "Debug", ui::Color::white(), nullptr, [&nav](){ nav.push<DebugMenuView>(); } },
|
||||
{ "HackRF mode", ui::Color::white(), &bitmap_icon_hackrf, [this, &nav](){ hackrf_mode(nav); } },
|
||||
|
191
firmware/application/ui_replay_view.cpp
Normal file
191
firmware/application/ui_replay_view.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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_replay_view.hpp"
|
||||
|
||||
#include "portapack.hpp"
|
||||
#include "message.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
using namespace portapack;
|
||||
|
||||
#include "time.hpp"
|
||||
|
||||
#include "string_format.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
|
||||
ReplayView::ReplayView(
|
||||
const Rect parent_rect,
|
||||
std::string filename_stem_pattern,
|
||||
const FileType file_type,
|
||||
const size_t read_size,
|
||||
const size_t buffer_count
|
||||
) : View { parent_rect },
|
||||
filename_stem_pattern { filename_stem_pattern },
|
||||
file_type { file_type },
|
||||
read_size { read_size },
|
||||
buffer_count { buffer_count }
|
||||
{
|
||||
add_children({ {
|
||||
&rect_background,
|
||||
&button_record,
|
||||
&text_replay_filename,
|
||||
&text_time_seek,
|
||||
} });
|
||||
|
||||
rect_background.set_parent_rect({ { 0, 0 }, size() });
|
||||
|
||||
button_record.on_select = [this](ImageButton&) {
|
||||
this->toggle();
|
||||
};
|
||||
|
||||
signal_token_tick_second = time::signal_tick_second += [this]() {
|
||||
this->on_tick_second();
|
||||
};
|
||||
}
|
||||
|
||||
ReplayView::~ReplayView() {
|
||||
time::signal_tick_second -= signal_token_tick_second;
|
||||
}
|
||||
|
||||
void ReplayView::focus() {
|
||||
button_record.focus();
|
||||
}
|
||||
|
||||
void ReplayView::set_sampling_rate(const size_t new_sampling_rate) {
|
||||
if( new_sampling_rate != sampling_rate ) {
|
||||
stop();
|
||||
sampling_rate = new_sampling_rate;
|
||||
|
||||
button_record.hidden(sampling_rate == 0);
|
||||
text_replay_filename.hidden(sampling_rate == 0);
|
||||
text_time_seek.hidden(sampling_rate == 0);
|
||||
rect_background.hidden(sampling_rate != 0);
|
||||
|
||||
update_status_display();
|
||||
}
|
||||
}
|
||||
|
||||
bool ReplayView::is_active() const {
|
||||
return (bool)replay_thread;
|
||||
}
|
||||
|
||||
void ReplayView::toggle() {
|
||||
if( is_active() ) {
|
||||
stop();
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
void ReplayView::start() {
|
||||
stop();
|
||||
|
||||
text_replay_filename.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<Reader> reader;
|
||||
auto p = std::make_unique<FileReader>();
|
||||
auto create_error = p->create(
|
||||
filename_stem + ".C16"
|
||||
);
|
||||
if( create_error.is_valid() ) {
|
||||
handle_error(create_error.value());
|
||||
} else {
|
||||
reader = std::move(p);
|
||||
}
|
||||
|
||||
if( reader ) {
|
||||
text_replay_filename.set(filename_stem);
|
||||
button_record.set_bitmap(&bitmap_stop);
|
||||
replay_thread = std::make_unique<ReplayThread>(
|
||||
std::move(reader),
|
||||
read_size, buffer_count,
|
||||
[]() {
|
||||
ReplayThreadDoneMessage message { };
|
||||
EventDispatcher::send_message(message);
|
||||
},
|
||||
[](File::Error error) {
|
||||
ReplayThreadDoneMessage message { error.code() };
|
||||
EventDispatcher::send_message(message);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
update_status_display();
|
||||
}
|
||||
|
||||
void ReplayView::stop() {
|
||||
if( is_active() ) {
|
||||
replay_thread.reset();
|
||||
button_record.set_bitmap(&bitmap_record);
|
||||
}
|
||||
|
||||
update_status_display();
|
||||
}
|
||||
|
||||
void ReplayView::on_tick_second() {
|
||||
update_status_display();
|
||||
}
|
||||
|
||||
void ReplayView::update_status_display() {
|
||||
/*if( sampling_rate ) {
|
||||
const auto space_info = std::filesystem::space("");
|
||||
const uint32_t bytes_per_second = file_type == FileType::WAV ? (sampling_rate * 2) : (sampling_rate * 4);
|
||||
const uint32_t available_seconds = space_info.free / bytes_per_second;
|
||||
const uint32_t seconds = available_seconds % 60;
|
||||
const uint32_t available_minutes = available_seconds / 60;
|
||||
const uint32_t minutes = available_minutes % 60;
|
||||
const uint32_t hours = available_minutes / 60;
|
||||
const std::string available_time =
|
||||
to_string_dec_uint(hours, 3, ' ') + ":" +
|
||||
to_string_dec_uint(minutes, 2, '0') + ":" +
|
||||
to_string_dec_uint(seconds, 2, '0');
|
||||
text_time_available.set(available_time);
|
||||
}*/
|
||||
}
|
||||
|
||||
void ReplayView::handle_replay_thread_done(const File::Error error) {
|
||||
stop();
|
||||
if( error.code() ) {
|
||||
handle_error(error);
|
||||
}
|
||||
}
|
||||
|
||||
void ReplayView::handle_error(const File::Error error) {
|
||||
if( on_error ) {
|
||||
on_error(error.what());
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
116
firmware/application/ui_replay_view.hpp
Normal file
116
firmware/application/ui_replay_view.hpp
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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_REPLAY_VIEW_H__
|
||||
#define __UI_REPLAY_VIEW_H__
|
||||
|
||||
#include "ui_widget.hpp"
|
||||
|
||||
#include "replay_thread.hpp"
|
||||
#include "signal.hpp"
|
||||
#include "bitmap.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
namespace ui {
|
||||
|
||||
class ReplayView : public View {
|
||||
public:
|
||||
std::function<void(std::string)> on_error;
|
||||
|
||||
enum FileType {
|
||||
RawS16 = 2,
|
||||
WAV = 3,
|
||||
};
|
||||
|
||||
ReplayView(
|
||||
const Rect parent_rect,
|
||||
std::string filename_stem_pattern,
|
||||
FileType file_type,
|
||||
const size_t read_size,
|
||||
const size_t buffer_count
|
||||
);
|
||||
~ReplayView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
void set_sampling_rate(const size_t new_sampling_rate);
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
bool is_active() const;
|
||||
|
||||
private:
|
||||
void toggle();
|
||||
|
||||
void on_tick_second();
|
||||
void update_status_display();
|
||||
|
||||
void handle_replay_thread_done(const File::Error error);
|
||||
void handle_error(const File::Error error);
|
||||
|
||||
bool pwmrssi_enabled = false;
|
||||
const std::string filename_stem_pattern;
|
||||
const FileType file_type;
|
||||
const size_t read_size;
|
||||
const size_t buffer_count;
|
||||
size_t sampling_rate { 0 };
|
||||
SignalToken signal_token_tick_second;
|
||||
|
||||
Rectangle rect_background {
|
||||
Color::black()
|
||||
};
|
||||
|
||||
ImageButton button_record {
|
||||
{ 4 * 8, 0 * 16, 2 * 8, 1 * 16 },
|
||||
&bitmap_record,
|
||||
Color::red(),
|
||||
Color::black()
|
||||
};
|
||||
|
||||
Text text_replay_filename {
|
||||
{ 7 * 8, 0 * 16, 8 * 8, 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
Text text_time_seek {
|
||||
{ 21 * 8, 0 * 16, 9 * 8, 16 },
|
||||
"",
|
||||
};
|
||||
|
||||
std::unique_ptr<ReplayThread> replay_thread;
|
||||
|
||||
MessageHandlerRegistration message_handler_capture_thread_error {
|
||||
Message::ID::CaptureThreadDone,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
|
||||
this->handle_replay_thread_done(message.error);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif/*__UI_REPLAY_VIEW_H__*/
|
@ -114,6 +114,7 @@ set(CPPSRC
|
||||
matched_filter.cpp
|
||||
spectrum_collector.cpp
|
||||
stream_input.cpp
|
||||
stream_output.cpp
|
||||
dsp_squelch.cpp
|
||||
clock_recovery.cpp
|
||||
packet_builder.cpp
|
||||
@ -368,12 +369,12 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PAFS afsk)
|
||||
|
||||
### Epar
|
||||
### Replay
|
||||
|
||||
#set(MODE_CPPSRC
|
||||
# proc_epar.cpp
|
||||
#)
|
||||
#DeclareTargets(PEPR epar)
|
||||
set(MODE_CPPSRC
|
||||
proc_replay.cpp
|
||||
)
|
||||
DeclareTargets(PREP replay)
|
||||
|
||||
### Tones
|
||||
|
||||
|
84
firmware/baseband/proc_replay.cpp
Normal file
84
firmware/baseband/proc_replay.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "proc_replay.hpp"
|
||||
|
||||
//#include "dsp_fir_taps.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
ReplayProcessor::ReplayProcessor() {
|
||||
|
||||
}
|
||||
|
||||
void ReplayProcessor::execute(const buffer_c8_t& buffer) {
|
||||
/* 2.4576MHz, 2048 samples */
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
|
||||
const auto& decimator_out = decim_1_out;
|
||||
const auto& channel = decimator_out;
|
||||
|
||||
if( stream ) {
|
||||
const size_t bytes_to_write = sizeof(*decimator_out.p) * decimator_out.count;
|
||||
const auto result = stream->write(decimator_out.p, bytes_to_write);
|
||||
}
|
||||
|
||||
feed_channel_stats(channel);
|
||||
|
||||
/*spectrum_samples += channel.count;
|
||||
if( spectrum_samples >= spectrum_interval_samples ) {
|
||||
spectrum_samples -= spectrum_interval_samples;
|
||||
channel_spectrum.feed(channel, channel_filter_pass_f, channel_filter_stop_f);
|
||||
}*/
|
||||
}
|
||||
|
||||
void ReplayProcessor::on_message(const Message* const message) {
|
||||
switch(message->id) {
|
||||
/*case Message::ID::UpdateSpectrum:
|
||||
case Message::ID::SpectrumStreamingConfig:
|
||||
channel_spectrum.on_message(message);
|
||||
break;*/
|
||||
|
||||
case Message::ID::ReplayConfig:
|
||||
replay_config(*reinterpret_cast<const ReplayConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ReplayProcessor::replay_config(const ReplayConfigMessage& message) {
|
||||
if( message.config ) {
|
||||
stream = std::make_unique<StreamOutput>(message.config);
|
||||
} else {
|
||||
stream.reset();
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher { std::make_unique<ReplayProcessor>() };
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
70
firmware/baseband/proc_replay.hpp
Normal file
70
firmware/baseband/proc_replay.hpp
Normal file
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __PROC_REPLAY_HPP__
|
||||
#define __PROC_REPLAY_HPP__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
//#include "rssi_thread.hpp"
|
||||
|
||||
//#include "dsp_decimate.hpp"
|
||||
|
||||
//#include "spectrum_collector.hpp"
|
||||
|
||||
#include "stream_output.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
class ReplayProcessor : public BasebandProcessor {
|
||||
public:
|
||||
ReplayProcessor();
|
||||
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
// 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;
|
||||
|
||||
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit };
|
||||
//RSSIThread rssi_thread { NORMALPRIO + 10 };
|
||||
|
||||
std::array<complex16_t, 512> dst;
|
||||
const buffer_c16_t dst_buffer {
|
||||
dst.data(),
|
||||
dst.size()
|
||||
};
|
||||
|
||||
std::unique_ptr<StreamOutput> stream;
|
||||
|
||||
/*SpectrumCollector channel_spectrum;
|
||||
size_t spectrum_interval_samples = 0;
|
||||
size_t spectrum_samples = 0;*/
|
||||
|
||||
void replay_config(const ReplayConfigMessage& message);
|
||||
};
|
||||
|
||||
#endif/*__PROC_REPLAY_HPP__*/
|
76
firmware/baseband/stream_output.cpp
Normal file
76
firmware/baseband/stream_output.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* 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 "stream_output.hpp"
|
||||
|
||||
#include "lpc43xx_cpp.hpp"
|
||||
using namespace lpc43xx;
|
||||
|
||||
StreamOutput::StreamOutput(ReplayConfig* const config) :
|
||||
fifo_buffers_empty { buffers_empty.data(), buffer_count_max_log2 },
|
||||
fifo_buffers_full { buffers_full.data(), buffer_count_max_log2 },
|
||||
config { config },
|
||||
data { std::make_unique<uint8_t[]>(config->read_size * config->buffer_count) }
|
||||
{
|
||||
config->fifo_buffers_empty = &fifo_buffers_empty;
|
||||
config->fifo_buffers_full = &fifo_buffers_full;
|
||||
|
||||
for(size_t i=0; i<config->buffer_count; i++) {
|
||||
buffers[i] = { &(data.get()[i * config->read_size]), config->read_size };
|
||||
fifo_buffers_empty.in(&buffers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
size_t StreamOutput::write(const void* const data, const size_t length) {
|
||||
const uint8_t* p = static_cast<const uint8_t*>(data);
|
||||
size_t written = 0;
|
||||
|
||||
while( written < length ) {
|
||||
if( !active_buffer ) {
|
||||
// We need an empty buffer...
|
||||
if( !fifo_buffers_empty.out(active_buffer) ) {
|
||||
// ...but none are available. Samples were dropped.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const auto remaining = length - written;
|
||||
written += active_buffer->write(&p[written], remaining);
|
||||
|
||||
if( active_buffer->is_full() ) {
|
||||
if( !fifo_buffers_full.in(active_buffer) ) {
|
||||
// FIFO is fuil of buffers, there's no place for this one.
|
||||
// Bail out of the loop, and try submitting the buffer in the
|
||||
// next pass.
|
||||
// This should never happen if the number of buffers is less
|
||||
// than the capacity of the FIFO.
|
||||
break;
|
||||
}
|
||||
active_buffer = nullptr;
|
||||
creg::m4txevent::assert();
|
||||
}
|
||||
}
|
||||
|
||||
config->baseband_bytes_sent += length;
|
||||
|
||||
return written;
|
||||
}
|
54
firmware/baseband/stream_output.hpp
Normal file
54
firmware/baseband/stream_output.hpp
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __STREAM_OUTPUT_H__
|
||||
#define __STREAM_OUTPUT_H__
|
||||
|
||||
#include "message.hpp"
|
||||
#include "fifo.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
class StreamOutput {
|
||||
public:
|
||||
StreamOutput(ReplayConfig* const config);
|
||||
|
||||
size_t write(const void* const data, const size_t length);
|
||||
|
||||
private:
|
||||
static constexpr size_t buffer_count_max_log2 = 3;
|
||||
static constexpr size_t buffer_count_max = 1U << buffer_count_max_log2;
|
||||
|
||||
FIFO<StreamBuffer*> fifo_buffers_empty;
|
||||
FIFO<StreamBuffer*> fifo_buffers_full;
|
||||
std::array<StreamBuffer, buffer_count_max> buffers;
|
||||
std::array<StreamBuffer*, buffer_count_max> buffers_empty;
|
||||
std::array<StreamBuffer*, buffer_count_max> buffers_full;
|
||||
StreamBuffer* active_buffer { nullptr };
|
||||
ReplayConfig* const config { nullptr };
|
||||
std::unique_ptr<uint8_t[]> data;
|
||||
};
|
||||
|
||||
#endif/*__STREAM_OUTPUT_H__*/
|
@ -66,23 +66,25 @@ public:
|
||||
DisplaySleep = 16,
|
||||
CaptureConfig = 17,
|
||||
CaptureThreadDone = 18,
|
||||
ReplayConfig = 19,
|
||||
ReplayThreadDone = 20,
|
||||
|
||||
TXDone = 20,
|
||||
Retune = 21,
|
||||
TonesConfigure = 22,
|
||||
AFSKConfigure = 23,
|
||||
PWMRSSIConfigure = 24,
|
||||
OOKConfigure = 25,
|
||||
RDSConfigure = 26,
|
||||
AudioTXConfig = 27,
|
||||
POCSAGConfigure = 28,
|
||||
DTMFTXConfig = 29,
|
||||
ADSBConfigure = 30,
|
||||
TXDone = 30,
|
||||
Retune = 31,
|
||||
TonesConfigure = 32,
|
||||
AFSKConfigure = 33,
|
||||
PWMRSSIConfigure = 34,
|
||||
OOKConfigure = 35,
|
||||
RDSConfigure = 36,
|
||||
AudioTXConfig = 37,
|
||||
POCSAGConfigure = 38,
|
||||
DTMFTXConfig = 39,
|
||||
ADSBConfigure = 40,
|
||||
|
||||
POCSAGPacket = 31,
|
||||
POCSAGPacket = 41,
|
||||
|
||||
FIFOSignal = 32,
|
||||
FIFOData = 33,
|
||||
FIFOSignal = 52,
|
||||
FIFOData = 53,
|
||||
MAX
|
||||
};
|
||||
|
||||
@ -492,6 +494,37 @@ public:
|
||||
CaptureConfig* const config;
|
||||
};
|
||||
|
||||
struct ReplayConfig {
|
||||
const size_t read_size;
|
||||
const size_t buffer_count;
|
||||
uint64_t baseband_bytes_sent;
|
||||
FIFO<StreamBuffer*>* fifo_buffers_empty;
|
||||
FIFO<StreamBuffer*>* fifo_buffers_full;
|
||||
|
||||
constexpr ReplayConfig(
|
||||
const size_t read_size,
|
||||
const size_t buffer_count
|
||||
) : read_size { read_size },
|
||||
buffer_count { buffer_count },
|
||||
baseband_bytes_sent { 0 },
|
||||
fifo_buffers_empty { nullptr },
|
||||
fifo_buffers_full { nullptr }
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class ReplayConfigMessage : public Message {
|
||||
public:
|
||||
constexpr ReplayConfigMessage(
|
||||
ReplayConfig* const config
|
||||
) : Message { ID::ReplayConfig },
|
||||
config { config }
|
||||
{
|
||||
}
|
||||
|
||||
ReplayConfig* const config;
|
||||
};
|
||||
|
||||
class TXDoneMessage : public Message {
|
||||
public:
|
||||
constexpr TXDoneMessage(
|
||||
@ -715,4 +748,16 @@ public:
|
||||
uint32_t error;
|
||||
};
|
||||
|
||||
class ReplayThreadDoneMessage : public Message {
|
||||
public:
|
||||
constexpr ReplayThreadDoneMessage(
|
||||
uint32_t error = 0
|
||||
) : Message { ID::ReplayThreadDone },
|
||||
error { error }
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t error;
|
||||
};
|
||||
|
||||
#endif/*__MESSAGE_H__*/
|
||||
|
@ -82,6 +82,7 @@ constexpr image_tag_t image_tag_tones { 'P', 'T', 'O', 'N' };
|
||||
constexpr image_tag_t image_tag_rds { 'P', 'R', 'D', 'S' };
|
||||
constexpr image_tag_t image_tag_ook { 'P', 'O', 'O', 'K' };
|
||||
constexpr image_tag_t image_tag_adsb_tx { 'P', 'A', 'D', 'S' };
|
||||
constexpr image_tag_t image_tag_replay { 'P', 'R', 'E', 'P' };
|
||||
|
||||
constexpr image_tag_t image_tag_hackrf { 'H', 'R', 'F', '1' };
|
||||
|
||||
|
BIN
firmware/graphics/PP_REPLAY.png
Normal file
BIN
firmware/graphics/PP_REPLAY.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 145 B |
Loading…
Reference in New Issue
Block a user