Merge branch 'master' into gps-sim

This commit is contained in:
Erwin Ried 2020-04-20 10:58:03 +02:00 committed by GitHub
commit b49d3160a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 2897 additions and 0 deletions

View File

@ -209,6 +209,7 @@ set(CPPSRC
ui/ui_btngrid.cpp ui/ui_btngrid.cpp
ui/ui_receiver.cpp ui/ui_receiver.cpp
ui/ui_rssi.cpp ui/ui_rssi.cpp
ui/ui_tv.cpp
ui/ui_spectrum.cpp ui/ui_spectrum.cpp
ui/ui_tabview.cpp ui/ui_tabview.cpp
ui/ui_textentry.cpp ui/ui_textentry.cpp
@ -217,6 +218,8 @@ set(CPPSRC
apps/ui_adsb_rx.cpp apps/ui_adsb_rx.cpp
apps/ui_adsb_tx.cpp apps/ui_adsb_tx.cpp
apps/ui_afsk_rx.cpp apps/ui_afsk_rx.cpp
apps/ui_btle_rx.cpp
apps/ui_nrf_rx.cpp
apps/ui_aprs_tx.cpp apps/ui_aprs_tx.cpp
apps/ui_bht_tx.cpp apps/ui_bht_tx.cpp
apps/ui_coasterp.cpp apps/ui_coasterp.cpp
@ -251,6 +254,7 @@ set(CPPSRC
apps/acars_app.cpp apps/acars_app.cpp
apps/ais_app.cpp apps/ais_app.cpp
apps/analog_audio_app.cpp apps/analog_audio_app.cpp
apps/analog_tv_app.cpp
apps/capture_app.cpp apps/capture_app.cpp
apps/ert_app.cpp apps/ert_app.cpp
apps/lge_app.cpp apps/lge_app.cpp

View File

@ -0,0 +1,238 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
* Copyright (C) 2020 Shao
*
* 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 "analog_tv_app.hpp"
#include "baseband_api.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
using namespace tonekey;
#include "audio.hpp"
#include "file.hpp"
#include "utility.hpp"
#include "string_format.hpp"
namespace ui {
/* AnalogTvView *******************************************************/
AnalogTvView::AnalogTvView(
NavigationView& nav
) : nav_ (nav)
{
add_children({
&rssi,
&channel,
&audio,
&field_frequency,
&field_lna,
&field_vga,
&options_modulation,
&field_volume,
&tv
});
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
this->field_frequency.set_value(f);
};
};
field_frequency.on_show_options = [this]() {
this->on_show_options_frequency();
};
field_lna.on_show_options = [this]() {
this->on_show_options_rf_gain();
};
field_vga.on_show_options = [this]() {
this->on_show_options_rf_gain();
};
const auto modulation = receiver_model.modulation();
options_modulation.set_by_value(toUType(ReceiverModel::Mode::WidebandFMAudio));
options_modulation.on_change = [this](size_t, OptionsField::value_t v) {
this->on_modulation_changed(static_cast<ReceiverModel::Mode>(v));
};
options_modulation.on_show_options = [this]() {
this->on_show_options_modulation();
};
field_volume.set_value(0);
field_volume.on_change = [this](int32_t v) {
this->on_headphone_volume_changed(v);
};
tv.on_select = [this](int32_t offset) {
field_frequency.set_value(receiver_model.tuning_frequency() + offset);
};
update_modulation(static_cast<ReceiverModel::Mode>(modulation));
on_modulation_changed(ReceiverModel::Mode::WidebandFMAudio);
}
AnalogTvView::~AnalogTvView() {
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
// both?
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
void AnalogTvView::on_hide() {
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
// it's being shown or hidden.
tv.on_hide();
View::on_hide();
}
void AnalogTvView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
const ui::Rect tv_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height };
tv.set_parent_rect(tv_rect);
}
void AnalogTvView::focus() {
field_frequency.focus();
}
void AnalogTvView::on_tuning_frequency_changed(rf::Frequency f) {
receiver_model.set_tuning_frequency(f);
}
void AnalogTvView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) {
receiver_model.set_baseband_bandwidth(bandwidth_hz);
}
void AnalogTvView::on_modulation_changed(const ReceiverModel::Mode modulation) {
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
// it's being shown or hidden.
tv.on_hide();
update_modulation(modulation);
on_show_options_modulation();
tv.on_show();
}
void AnalogTvView::remove_options_widget() {
if( options_widget ) {
remove_child(options_widget.get());
options_widget.reset();
}
field_lna.set_style(nullptr);
options_modulation.set_style(nullptr);
field_frequency.set_style(nullptr);
}
void AnalogTvView::set_options_widget(std::unique_ptr<Widget> new_widget) {
remove_options_widget();
if( new_widget ) {
options_widget = std::move(new_widget);
} else {
// TODO: Lame hack to hide options view due to my bad paint/damage algorithm.
options_widget = std::make_unique<Rectangle>(options_view_rect, style_options_group_new.background);
}
add_child(options_widget.get());
}
void AnalogTvView::on_show_options_frequency() {
auto widget = std::make_unique<FrequencyOptionsView>(options_view_rect, &style_options_group_new);
widget->set_step(receiver_model.frequency_step());
widget->on_change_step = [this](rf::Frequency f) {
this->on_frequency_step_changed(f);
};
widget->set_reference_ppm_correction(persistent_memory::correction_ppb() / 1000);
widget->on_change_reference_ppm_correction = [this](int32_t v) {
this->on_reference_ppm_correction_changed(v);
};
set_options_widget(std::move(widget));
field_frequency.set_style(&style_options_group_new);
}
void AnalogTvView::on_show_options_rf_gain() {
auto widget = std::make_unique<RadioGainOptionsView>(options_view_rect, &style_options_group_new);
set_options_widget(std::move(widget));
field_lna.set_style(&style_options_group_new);
}
void AnalogTvView::on_show_options_modulation() {
std::unique_ptr<Widget> widget;
const auto modulation = static_cast<ReceiverModel::Mode>(receiver_model.modulation());
tv.show_audio_spectrum_view(true);
set_options_widget(std::move(widget));
options_modulation.set_style(&style_options_group_new);
}
void AnalogTvView::on_frequency_step_changed(rf::Frequency f) {
receiver_model.set_frequency_step(f);
field_frequency.set_step(f);
}
void AnalogTvView::on_reference_ppm_correction_changed(int32_t v) {
persistent_memory::set_correction_ppb(v * 1000);
}
void AnalogTvView::on_headphone_volume_changed(int32_t v) {
//tv::TVView::set_headphone_volume(this,v);
}
void AnalogTvView::update_modulation(const ReceiverModel::Mode modulation) {
audio::output::mute();
baseband::shutdown();
portapack::spi_flash::image_tag_t image_tag;
image_tag = portapack::spi_flash::image_tag_am_tv;
baseband::run_image(image_tag);
receiver_model.set_modulation(modulation);
receiver_model.set_sampling_rate(2000000);
receiver_model.set_baseband_bandwidth(2000000);
receiver_model.enable();
}
} /* namespace ui */

View File

@ -0,0 +1,133 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2018 Furrtek
* Copyright (C) 2020 Shao
*
* 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 __ANALOG_TV_APP_H__
#define __ANALOG_TV_APP_H__
#include "receiver_model.hpp"
#include "ui_receiver.hpp"
#include "ui_tv.hpp"
#include "ui_record_view.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "tone_key.hpp"
namespace ui {
constexpr Style style_options_group_new {
.font = font::fixed_8x16,
.background = Color::blue(),
.foreground = Color::white(),
};
class AnalogTvView : public View {
public:
AnalogTvView(NavigationView& nav);
~AnalogTvView();
void on_hide() override;
void set_parent_rect(const Rect new_parent_rect) override;
void focus() override;
std::string title() const override { return "Analog TV"; };
private:
static constexpr ui::Dim header_height = 3 * 16;
const Rect options_view_rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 };
const Rect nbfm_view_rect { 0 * 8, 1 * 16, 18 * 8, 1 * 16 };
NavigationView& nav_;
RSSI rssi {
{ 21 * 8, 0, 6 * 8, 4 },
};
Channel channel {
{ 21 * 8, 5, 6 * 8, 4 },
};
Audio audio {
{ 21 * 8, 10, 6 * 8, 4 },
};
FrequencyField field_frequency {
{ 5 * 8, 0 * 16 },
};
LNAGainField field_lna {
{ 15 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 18 * 8, 0 * 16 }
};
OptionsField options_modulation {
{ 0 * 8, 0 * 16 },
4,
{
{ "TV ", toUType(ReceiverModel::Mode::WidebandFMAudio) },
{ "TV ", toUType(ReceiverModel::Mode::WidebandFMAudio) },
{ "TV ", toUType(ReceiverModel::Mode::WidebandFMAudio) },
}
};
NumberField field_volume {
{ 27 * 8, 0 * 16 },
3,
{ 0, 255 },
1,
' ',
};
std::unique_ptr<Widget> options_widget { };
tv::TVWidget tv { true };
void on_tuning_frequency_changed(rf::Frequency f);
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
void on_modulation_changed(const ReceiverModel::Mode modulation);
void on_show_options_frequency();
void on_show_options_rf_gain();
void on_show_options_modulation();
void on_frequency_step_changed(rf::Frequency f);
void on_reference_ppm_correction_changed(int32_t v);
void on_headphone_volume_changed(int32_t v);
void on_edit_frequency();
void remove_options_widget();
void set_options_widget(std::unique_ptr<Widget> new_widget);
void update_modulation(const ReceiverModel::Mode modulation);
};
} /* namespace ui */
#endif/*__ANALOG_TV_APP_H__*/

View File

@ -0,0 +1,160 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2020 Shao
*
* 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_btle_rx.hpp"
#include "ui_modemsetup.hpp"
#include "modems.hpp"
#include "audio.hpp"
#include "rtc_time.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
using namespace modems;
namespace ui {
void BTLERxView::focus() {
field_frequency.focus();
}
void BTLERxView::update_freq(rf::Frequency f) {
receiver_model.set_tuning_frequency(f);
}
BTLERxView::BTLERxView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_btle_rx);
add_children({
&rssi,
&channel,
&field_rf_amp,
&field_lna,
&field_vga,
&field_frequency,
&text_debug,
&button_modem_setup,
&record_view,
&console
});
// DEBUG
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
record_view.set_sampling_rate(24000);
// Auto-configure modem for LCR RX (will be removed later)
update_freq(2426000000);
auto def_bell202 = &modem_defs[0];
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
serial_format_t serial_format;
serial_format.data_bits = 7;
serial_format.parity = EVEN;
serial_format.stop_bits = 1;
serial_format.bit_order = LSB_FIRST;
persistent_memory::set_serial_format(serial_format);
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_step(100);
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
update_freq(f);
field_frequency.set_value(f);
};
};
button_modem_setup.on_select = [&nav](Button&) {
nav.push<ModemSetupView>();
};
// Auto-configure modem for LCR RX (will be removed later)
baseband::set_btle(persistent_memory::modem_baudrate(), 8, 0, false);
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
receiver_model.set_sampling_rate(4000000);
receiver_model.set_baseband_bandwidth(4000000);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
receiver_model.enable();
}
void BTLERxView::on_data(uint32_t value, bool is_data) {
//std::string str_console = "\x1B";
std::string str_console = "";
if (is_data) {
// Colorize differently after message splits
//str_console += (char)((console_color & 3) + 9);
//value &= 0xFF; // ABCDEFGH
//value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); // EFGHABCD
//value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); // GHEFCDAB
//value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); // HGFEDCBA
//value &= 0x7F; // Ignore parity, which is the MSB now
//if ((value >= 32) && (value < 127)) {
// str_console += (char)value; // Printable
//}
//str_console += (char)'A';
//str_console += (char)value;
//str_console += "[" + to_string_hex(value, 2) + "]";
str_console += ":" + to_string_hex(value, 2) ;
console.write(str_console);
/*if ((value != 0x7F) && (prev_value == 0x7F)) {
// Message split
console.writeln("");
console_color++;
}*/
//prev_value = value;
} else {
// Baudrate estimation
//text_debug.set("~" + to_string_dec_uint(value));
if (value == 'A')
{console.write("mac");}
else if (value == 'B')
{console.writeln("");}
//console.writeln("");
}
}
BTLERxView::~BTLERxView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
} /* namespace ui */

View File

@ -0,0 +1,107 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2020 Shao
*
* 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_BTLE_RX_H__
#define __UI_BTLE_RX_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_record_view.hpp" // DEBUG
#include "utility.hpp"
namespace ui {
class BTLERxView : public View {
public:
BTLERxView(NavigationView& nav);
~BTLERxView();
void focus() override;
std::string title() const override { return "BTLE RX"; };
private:
void on_data(uint32_t value, bool is_data);
uint8_t console_color { 0 };
uint32_t prev_value { 0 };
std::string str_log { "" };
RFAmpField field_rf_amp {
{ 13 * 8, 0 * 16 }
};
LNAGainField field_lna {
{ 15 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 18 * 8, 0 * 16 }
};
RSSI rssi {
{ 21 * 8, 0, 6 * 8, 4 },
};
Channel channel {
{ 21 * 8, 5, 6 * 8, 4 },
};
FrequencyField field_frequency {
{ 0 * 8, 0 * 16 },
};
Text text_debug {
{ 0 * 8, 1 * 16, 10 * 8, 16 },
"DEBUG"
};
Button button_modem_setup {
{ 12 * 8, 1 * 16, 96, 24 },
"Modem setup"
};
// DEBUG
RecordView record_view {
{ 0 * 8, 3 * 16, 30 * 8, 1 * 16 },
u"AFS_????", RecordView::FileType::WAV, 4096, 4
};
Console console {
{ 0, 4 * 16, 240, 240 }
};
void update_freq(rf::Frequency f);
//void on_data_afsk(const AFSKDataMessage& message);
MessageHandlerRegistration message_handler_packet {
Message::ID::AFSKData,
[this](Message* const p) {
const auto message = static_cast<const AFSKDataMessage*>(p);
this->on_data(message->value, message->is_data);
}
};
};
} /* namespace ui */
#endif/*__UI_BTLE_RX_H__*/

View File

@ -0,0 +1,165 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2020 Shao
*
* 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_nrf_rx.hpp"
#include "ui_modemsetup.hpp"
#include "modems.hpp"
#include "audio.hpp"
#include "rtc_time.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
using namespace modems;
namespace ui {
void NRFRxView::focus() {
field_frequency.focus();
}
void NRFRxView::update_freq(rf::Frequency f) {
receiver_model.set_tuning_frequency(f);
}
NRFRxView::NRFRxView(NavigationView& nav) {
baseband::run_image(portapack::spi_flash::image_tag_nrf_rx);
add_children({
&rssi,
&channel,
&field_rf_amp,
&field_lna,
&field_vga,
&field_frequency,
&text_debug,
&button_modem_setup,
&record_view,
&console
});
// DEBUG
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
record_view.set_sampling_rate(24000);
// Auto-configure modem for LCR RX (will be removed later)
update_freq(2480000000);
auto def_bell202 = &modem_defs[0];
persistent_memory::set_modem_baudrate(def_bell202->baudrate);
serial_format_t serial_format;
serial_format.data_bits = 7;
serial_format.parity = EVEN;
serial_format.stop_bits = 1;
serial_format.bit_order = LSB_FIRST;
persistent_memory::set_serial_format(serial_format);
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_step(100);
field_frequency.on_change = [this](rf::Frequency f) {
update_freq(f);
};
field_frequency.on_edit = [this, &nav]() {
auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
new_view->on_changed = [this](rf::Frequency f) {
update_freq(f);
field_frequency.set_value(f);
};
};
button_modem_setup.on_select = [&nav](Button&) {
nav.push<ModemSetupView>();
};
// Auto-configure modem for LCR RX (will be removed later)
baseband::set_nrf(persistent_memory::modem_baudrate(), 8, 0, false);
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
receiver_model.set_sampling_rate(4000000);
receiver_model.set_baseband_bandwidth(4000000);
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
receiver_model.enable();
}
void NRFRxView::on_data(uint32_t value, bool is_data) {
//std::string str_console = "\x1B";
std::string str_console = "";
if (is_data) {
// Colorize differently after message splits
//str_console += (char)((console_color & 3) + 9);
//value &= 0xFF; // ABCDEFGH
//value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); // EFGHABCD
//value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); // GHEFCDAB
//value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); // HGFEDCBA
//value &= 0x7F; // Ignore parity, which is the MSB now
//if ((value >= 32) && (value < 127)) {
// str_console += (char)value; // Printable
//}
//str_console += (char)'A';
//str_console += (char)value;
//str_console += "[" + to_string_hex(value, 2) + "]";
str_console += " " + to_string_hex(value, 2) ;
console.write(str_console);
/*if ((value != 0x7F) && (prev_value == 0x7F)) {
// Message split
console.writeln("");
console_color++;
}*/
//prev_value = value;
} else {
// Baudrate estimation
//text_debug.set("~" + to_string_dec_uint(value));
if (value == 'A')
{console.write("addr:");}
else if (value == 'B')
{console.write(" data:");}
else if (value == 'C')
{
console.writeln("");
console.writeln("");
}
//console.writeln("");
}
}
NRFRxView::~NRFRxView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
} /* namespace ui */

View File

@ -0,0 +1,107 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
* Copyright (C) 2020 Shao
*
* 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_NRF_RX_H__
#define __UI_NRF_RX_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_record_view.hpp" // DEBUG
#include "utility.hpp"
namespace ui {
class NRFRxView : public View {
public:
NRFRxView(NavigationView& nav);
~NRFRxView();
void focus() override;
std::string title() const override { return "NRF RX"; };
private:
void on_data(uint32_t value, bool is_data);
uint8_t console_color { 0 };
uint32_t prev_value { 0 };
std::string str_log { "" };
RFAmpField field_rf_amp {
{ 13 * 8, 0 * 16 }
};
LNAGainField field_lna {
{ 15 * 8, 0 * 16 }
};
VGAGainField field_vga {
{ 18 * 8, 0 * 16 }
};
RSSI rssi {
{ 21 * 8, 0, 6 * 8, 4 },
};
Channel channel {
{ 21 * 8, 5, 6 * 8, 4 },
};
FrequencyField field_frequency {
{ 0 * 8, 0 * 16 },
};
Text text_debug {
{ 0 * 8, 1 * 16, 10 * 8, 16 },
"DEBUG"
};
Button button_modem_setup {
{ 12 * 8, 1 * 16, 96, 24 },
"Modem setup"
};
// DEBUG
RecordView record_view {
{ 0 * 8, 3 * 16, 30 * 8, 1 * 16 },
u"AFS_????", RecordView::FileType::WAV, 4096, 4
};
Console console {
{ 0, 4 * 16, 240, 240 }
};
void update_freq(rf::Frequency f);
//void on_data_afsk(const AFSKDataMessage& message);
MessageHandlerRegistration message_handler_packet {
Message::ID::AFSKData,
[this](Message* const p) {
const auto message = static_cast<const AFSKDataMessage*>(p);
this->on_data(message->value, message->is_data);
}
};
};
} /* namespace ui */
#endif/*__UI_NRF_RX_H__*/

View File

@ -130,6 +130,26 @@ void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_
send_message(&message); send_message(&message);
} }
void set_btle(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) {
const BTLERxConfigureMessage message {
baudrate,
word_length,
trigger_value,
trigger_word
};
send_message(&message);
}
void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) {
const NRFRxConfigureMessage message {
baudrate,
word_length,
trigger_value,
trigger_word
};
send_message(&message);
}
void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space, void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space,
const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count) { const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count) {
const AFSKTxConfigureMessage message { const AFSKTxConfigureMessage message {

View File

@ -68,6 +68,11 @@ void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phas
const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count); const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count);
void kill_afsk(); void kill_afsk();
void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word); void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
void set_btle(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word);
void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat, void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat,
const uint32_t pause_symbols); const uint32_t pause_symbols);
void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift, void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift,

View File

@ -1524,6 +1524,51 @@ static constexpr uint8_t bitmap_gps_sim_data[] = {
static constexpr Bitmap bitmap_gps_sim { static constexpr Bitmap bitmap_gps_sim {
{ 16, 16 }, bitmap_gps_sim_data { 16, 16 }, bitmap_gps_sim_data
}; };
static constexpr uint8_t bitmap_icon_btle_data[] = {
0x00, 0x00,
0x80, 0x00,
0x80, 0x01,
0x80, 0x07,
0x80, 0x0C,
0x98, 0x06,
0xF0, 0x03,
0xE0, 0x01,
0xE0, 0x01,
0xF0, 0x03,
0x98, 0x06,
0x80, 0x0C,
0x80, 0x07,
0x80, 0x01,
0x80, 0x00,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_btle {
{ 16, 16 }, bitmap_icon_btle_data
};
static constexpr uint8_t bitmap_icon_nrf_data[] = {
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0x07, 0xE0,
0x05, 0x20,
0xCC, 0x33,
0xF4, 0x2F,
0xE6, 0x67,
0xE2, 0x47,
0x36, 0x5C,
0x0E, 0xE0,
0x06, 0x60,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
0x00, 0x00,
};
static constexpr Bitmap bitmap_icon_nrf {
{ 16, 16 }, bitmap_icon_nrf_data
};
static constexpr uint8_t bitmap_sig_sine_data[] = { static constexpr uint8_t bitmap_sig_sine_data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

View File

@ -538,3 +538,262 @@ const std::array<ui::Color, 256> spectrum_rgb3_lut { {
{ 252, 3, 0 }, { 252, 3, 0 },
{ 255, 0, 0 }, { 255, 0, 0 },
} }; } };
const std::array<ui::Color, 256> spectrum_rgb4_lut { {
{ 0, 0, 0 },
{ 1, 1, 1 },
{ 2, 2, 2 },
{ 3, 3, 3 },
{ 4, 4, 4 },
{ 5, 5, 5 },
{ 6, 6, 6 },
{ 7, 7, 7 },
{ 8, 8, 8 },
{ 9, 9, 9 },
{ 10, 10, 10 },
{ 11, 11, 11 },
{ 12, 12, 12 },
{ 13, 13, 13 },
{ 14, 14, 14 },
{ 15, 15, 15 },
{ 16, 16, 16 },
{ 17, 17, 17 },
{ 18, 18, 18 },
{ 19, 19, 19 },
{ 20, 20, 20 },
{ 21, 21, 21 },
{ 22, 22, 22 },
{ 23, 23, 23 },
{ 24, 24, 24 },
{ 25, 25, 25 },
{ 26, 26, 26 },
{ 27, 27, 27 },
{ 28, 28, 28 },
{ 29, 29, 29 },
{ 30, 30, 30 },
{ 31, 31, 31 },
{ 32, 32, 32 },
{ 33, 33, 33 },
{ 34, 34, 34 },
{ 35, 35, 35 },
{ 36, 36, 36 },
{ 37, 37, 37 },
{ 38, 38, 38 },
{ 39, 39, 39 },
{ 40, 40, 40 },
{ 41, 41, 41 },
{ 42, 42, 42 },
{ 43, 43, 43 },
{ 44, 44, 44 },
{ 45, 45, 45 },
{ 46, 46, 46 },
{ 47, 47, 47 },
{ 48, 48, 48 },
{ 49, 49, 49 },
{ 50, 50, 50 },
{ 51, 51, 51 },
{ 52, 52, 52 },
{ 53, 53, 53 },
{ 54, 54, 54 },
{ 55, 55, 55 },
{ 56, 56, 56 },
{ 57, 57, 57 },
{ 58, 58, 58 },
{ 59, 59, 59 },
{ 60, 60, 60 },
{ 61, 61, 61 },
{ 62, 62, 62 },
{ 63, 63, 63 },
{ 64, 64, 64 },
{ 65, 65, 65 },
{ 66, 66, 66 },
{ 67, 67, 67 },
{ 68, 68, 68 },
{ 69, 69, 69 },
{ 70, 70, 70 },
{ 71, 71, 71 },
{ 72, 72, 72 },
{ 73, 73, 73 },
{ 74, 74, 74 },
{ 75, 75, 75 },
{ 76, 76, 76 },
{ 77, 77, 77 },
{ 78, 78, 78 },
{ 79, 79, 79 },
{ 80, 80, 80 },
{ 81, 81, 81 },
{ 82, 82, 82 },
{ 83, 83, 83 },
{ 84, 84, 84 },
{ 85, 85, 85 },
{ 86, 86, 86 },
{ 87, 87, 87 },
{ 88, 88, 88 },
{ 89, 89, 89 },
{ 90, 90, 90 },
{ 91, 91, 91 },
{ 92, 92, 92 },
{ 93, 93, 93 },
{ 94, 94, 94 },
{ 95, 95, 95 },
{ 96, 96, 96 },
{ 97, 97, 97 },
{ 98, 98, 98 },
{ 99, 99, 99 },
{ 100, 100, 100 },
{ 101, 101, 101 },
{ 102, 102, 102 },
{ 103, 103, 103 },
{ 104, 104, 104 },
{ 105, 105, 105 },
{ 106, 106, 106 },
{ 107, 107, 107 },
{ 108, 108, 108 },
{ 109, 109, 109 },
{ 110, 110, 110 },
{ 111, 111, 111 },
{ 112, 112, 112 },
{ 113, 113, 113 },
{ 114, 114, 114 },
{ 115, 115, 115 },
{ 116, 116, 116 },
{ 117, 117, 117 },
{ 118, 118, 118 },
{ 119, 119, 119 },
{ 120, 120, 120 },
{ 121, 121, 121 },
{ 122, 122, 122 },
{ 123, 123, 123 },
{ 124, 124, 124 },
{ 125, 125, 125 },
{ 126, 126, 126 },
{ 127, 127, 127 },
{ 128, 128, 128 },
{ 129, 129, 129 },
{ 130, 130, 130 },
{ 131, 131, 131 },
{ 132, 132, 132 },
{ 133, 133, 133 },
{ 134, 134, 134 },
{ 135, 135, 135 },
{ 136, 136, 136 },
{ 137, 137, 137 },
{ 138, 138, 138 },
{ 139, 139, 139 },
{ 140, 140, 140 },
{ 141, 141, 141 },
{ 142, 142, 142 },
{ 143, 143, 143 },
{ 144, 144, 144 },
{ 145, 145, 145 },
{ 146, 146, 146 },
{ 147, 147, 147 },
{ 148, 148, 148 },
{ 149, 149, 149 },
{ 150, 150, 150 },
{ 151, 151, 151 },
{ 152, 152, 152 },
{ 153, 153, 153 },
{ 154, 154, 154 },
{ 155, 155, 155 },
{ 156, 156, 156 },
{ 157, 157, 157 },
{ 158, 158, 158 },
{ 159, 159, 159 },
{ 160, 160, 160 },
{ 161, 161, 161 },
{ 162, 162, 162 },
{ 163, 163, 163 },
{ 164, 164, 164 },
{ 165, 165, 165 },
{ 166, 166, 166 },
{ 167, 167, 167 },
{ 168, 168, 168 },
{ 169, 169, 169 },
{ 170, 170, 170 },
{ 171, 171, 171 },
{ 172, 172, 172 },
{ 173, 173, 173 },
{ 174, 174, 174 },
{ 175, 175, 175 },
{ 176, 176, 176 },
{ 177, 177, 177 },
{ 178, 178, 178 },
{ 179, 179, 179 },
{ 180, 180, 180 },
{ 181, 181, 181 },
{ 182, 182, 182 },
{ 183, 183, 183 },
{ 184, 184, 184 },
{ 185, 185, 185 },
{ 186, 186, 186 },
{ 187, 187, 187 },
{ 188, 188, 188 },
{ 189, 189, 189 },
{ 190, 190, 190 },
{ 191, 191, 191 },
{ 192, 192, 192 },
{ 193, 193, 193 },
{ 194, 194, 194 },
{ 195, 195, 195 },
{ 196, 196, 196 },
{ 197, 197, 197 },
{ 198, 198, 198 },
{ 199, 199, 199 },
{ 200, 200, 200 },
{ 201, 201, 201 },
{ 202, 202, 202 },
{ 203, 203, 203 },
{ 204, 204, 204 },
{ 205, 205, 205 },
{ 206, 206, 206 },
{ 207, 207, 207 },
{ 208, 208, 208 },
{ 209, 209, 209 },
{ 210, 210, 210 },
{ 211, 211, 211 },
{ 212, 212, 212 },
{ 213, 213, 213 },
{ 214, 214, 214 },
{ 215, 215, 215 },
{ 216, 216, 216 },
{ 217, 217, 217 },
{ 218, 218, 218 },
{ 219, 219, 219 },
{ 220, 220, 220 },
{ 221, 221, 221 },
{ 222, 222, 222 },
{ 223, 223, 223 },
{ 224, 224, 224 },
{ 225, 225, 225 },
{ 226, 226, 226 },
{ 227, 227, 227 },
{ 228, 228, 228 },
{ 229, 229, 229 },
{ 230, 230, 230 },
{ 231, 231, 231 },
{ 232, 232, 232 },
{ 233, 233, 233 },
{ 234, 234, 234 },
{ 235, 235, 235 },
{ 236, 236, 236 },
{ 237, 237, 237 },
{ 238, 238, 238 },
{ 239, 239, 239 },
{ 240, 240, 240 },
{ 241, 241, 241 },
{ 242, 242, 242 },
{ 243, 243, 243 },
{ 244, 244, 244 },
{ 245, 245, 245 },
{ 246, 246, 246 },
{ 247, 247, 247 },
{ 248, 248, 248 },
{ 249, 249, 249 },
{ 250, 250, 250 },
{ 251, 251, 251 },
{ 252, 252, 252 },
{ 253, 253, 253 },
{ 254, 254, 254 },
{ 255, 255, 255 },
} };

View File

@ -28,5 +28,6 @@
extern const std::array<ui::Color, 256> spectrum_rgb2_lut; extern const std::array<ui::Color, 256> spectrum_rgb2_lut;
extern const std::array<ui::Color, 256> spectrum_rgb3_lut; extern const std::array<ui::Color, 256> spectrum_rgb3_lut;
extern const std::array<ui::Color, 256> spectrum_rgb4_lut;
#endif/*__SPECTRUM_COLOR_LUT_H__*/ #endif/*__SPECTRUM_COLOR_LUT_H__*/

View File

@ -0,0 +1,251 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2020 Shao
*
* 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_tv.hpp"
#include "spectrum_color_lut.hpp"
#include "portapack.hpp"
using namespace portapack;
#include "baseband_api.hpp"
#include "string_format.hpp"
#include <cmath>
#include <array>
namespace ui {
namespace tv {
/* TimeScopeView******************************************************/
TimeScopeView::TimeScopeView(
const Rect parent_rect
) : View { parent_rect }
{
set_focusable(true);
add_children({
//&labels,
//&field_frequency,
&waveform
});
/*field_frequency.on_change = [this](int32_t) {
set_dirty();
};
field_frequency.set_value(10);*/
}
void TimeScopeView::paint(Painter& painter) {
const auto r = screen_rect();
painter.fill_rectangle(r, Color::black());
// Cursor
/*
const Rect r_cursor {
field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height,
1, cursor_band_height
};
painter.fill_rectangle(
r_cursor,
Color::red()
);*/
}
void TimeScopeView::on_audio_spectrum(const AudioSpectrum* spectrum) {
for (size_t i = 0; i < spectrum->db.size(); i++)
audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256;
waveform.set_dirty();
}
/* TVView *********************************************************/
void TVView::on_show() {
clear();
const auto screen_r = screen_rect();
display.scroll_set_area(screen_r.top(), screen_r.bottom());
}
void TVView::on_hide() {
/* TODO: Clear region to eliminate brief flash of content at un-shifted
* position?
*/
display.scroll_disable();
}
void TVView::paint(Painter& painter) {
// Do nothing.
(void)painter;
}
void TVView::on_adjust_xcorr(uint8_t xcorr){
x_correction = xcorr;
}
void TVView::on_channel_spectrum(
const ChannelSpectrum& spectrum
) {
//portapack has limitations
// 1.screen resolution (less than 240x320) 2.samples each call back (128 or 256)
// 3.memory size (for ui::Color, the buffer size
//spectrum.db[i] is 256 long
//768x625 ->128x625 ->128x312 -> 128x104
//originally @6MHz sample rate, the PAL should be 768x625
//I reduced sample rate to 2MHz(3 times less samples), then calculate mag (effectively decimate by 2)
//the resolution is now changed to 128x625. The total decimation factor is 6, which changes how many samples in a line
//However 625 is too large for the screen, also interlaced scanning is harder to realize in portapack than normal computer.
//So I decided to simply drop half of the lines, once y is larger than 625/2=312.5 or 312, I recognize it as a new frame.
//then the resolution is changed to 128x312
//128x312 is now able to put into a 240x320 screen, but the buffer for a whole frame is 128x312=39936, which is too large
//according to my test, I can only make a buffer with a length of 13312 of type ui::Color. which is 1/3 of what I wanted.
//So now the resolution is changed to 128x104, the height is shrinked to 1/3 of the original height.
//I was expecting to see 1/3 height of original video.
//Look how nice is that! I am now able to meet the requirements of 1 and 3 for portapack. Also the length of a line is 128
//Each call back gives me 256 samples which is exactly 2 lines. What a coincidence!
//After some experiment, I did some improvements.
//1.I found that instead of 1/3 of the frame is shown, I got 3 whole frames shrinked into one window.
//So I made the height twice simply by painting 2 identical lines in the place of original lines
//2.I found sometimes there is an horizontal offset, so I added x_correction to move the frame back to center manually
//3.I changed video_buffer's type, from ui::Color to uint_8, since I don't need 3 digit to represent a grey scale value.
//I was hoping that by doing this, I can have a longer buffer like 39936, then the frame will looks better vertically
//however this is useless until now.
for(size_t i=0; i<256; i++)
{
//video_buffer[i+count*256] = spectrum_rgb4_lut[spectrum.db[i]];
video_buffer_int[i+count*256] = 255 - spectrum.db[i];
}
count = count + 1;
if (count == 52 -1)
{
ui::Color line_buffer[128];
Coord line;
uint32_t bmp_px;
/*for (line = 0; line < 104; line++)
{
for (bmp_px = 0; bmp_px < 128; bmp_px++)
{
//line_buffer[bmp_px] = video_buffer[bmp_px+line*128];
line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line*128 + x_correction]];
}
display.render_line({ 0, line + 100 }, 128, line_buffer);
}*/
for (line = 0; line < 208; line=line+2)
{
for (bmp_px = 0; bmp_px < 128; bmp_px++)
{
//line_buffer[bmp_px] = video_buffer[bmp_px+line*128];
line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line/2*128 + x_correction]];
}
display.render_line({ 0, line + 100 }, 128, line_buffer);
display.render_line({ 0, line + 101 }, 128, line_buffer);
}
count = 0;
}
}
void TVView::clear() {
display.fill_rectangle(
screen_rect(),
Color::black()
);
}
/* TVWidget *******************************************************/
TVWidget::TVWidget(const bool cursor) {
add_children({
&tv_view,
&field_xcorr
});
field_xcorr.set_value(10);
}
void TVWidget::on_show() {
baseband::spectrum_streaming_start();
}
void TVWidget::on_hide() {
baseband::spectrum_streaming_stop();
}
void TVWidget::show_audio_spectrum_view(const bool show) {
if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return;
if (show) {
audio_spectrum_view = std::make_unique<TimeScopeView>(audio_spectrum_view_rect);
add_child(audio_spectrum_view.get());
update_widgets_rect();
} else {
audio_spectrum_update = false;
remove_child(audio_spectrum_view.get());
audio_spectrum_view.reset();
update_widgets_rect();
}
}
void TVWidget::update_widgets_rect() {
if (audio_spectrum_view) {
tv_view.set_parent_rect(tv_reduced_rect);
} else {
tv_view.set_parent_rect(tv_normal_rect);
}
tv_view.on_show();
}
void TVWidget::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
tv_normal_rect = { 0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height};
tv_reduced_rect = { 0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height };
update_widgets_rect();
}
void TVWidget::paint(Painter& painter) {
// TODO:
(void)painter;
}
void TVWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) {
tv_view.on_channel_spectrum(spectrum);
tv_view.on_adjust_xcorr(field_xcorr.value());
sampling_rate = spectrum.sampling_rate;
}
void TVWidget::on_audio_spectrum() {
audio_spectrum_view->on_audio_spectrum(audio_spectrum_data);
}
} /* namespace tv */
} /* namespace ui */

View File

@ -0,0 +1,176 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2020 Shao
*
* 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_TV_H__
#define __UI_TV_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "event_m0.hpp"
#include "message.hpp"
#include <cstdint>
#include <cstddef>
namespace ui {
namespace tv {
class TimeScopeView : public View {
public:
TimeScopeView(const Rect parent_rect);
void paint(Painter& painter) override;
void on_audio_spectrum(const AudioSpectrum* spectrum);
private:
static constexpr int cursor_band_height = 4;
int16_t audio_spectrum[128] { 0 };
/*Labels labels {
{ { 6 * 8, 0 * 16 }, "Hz", Color::light_grey() }
};*/
/*
NumberField field_frequency {
{ 0 * 8, 0 * 16 },
5,
{ 0, 48000 },
48000 / 240,
' '
};*/
Waveform waveform {
{ 0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16 },
audio_spectrum,
128,
0,
false,
Color::white()
};
};
class TVView : public Widget {
public:
void on_show() override;
void on_hide() override;
void paint(Painter& painter) override;
void on_channel_spectrum(const ChannelSpectrum& spectrum);
void on_adjust_xcorr(uint8_t xcorr);
//ui::Color video_buffer[13312];
uint8_t video_buffer_int[13312+128] { 0 }; //128 is for the over length caused by x_correction
uint32_t count=0;
uint8_t x_correction=0;
private:
void clear();
};
class TVWidget : public View {
public:
std::function<void(int32_t offset)> on_select { };
TVWidget(const bool cursor = false);
TVWidget(const TVWidget&) = delete;
TVWidget(TVWidget&&) = delete;
TVWidget& operator=(const TVWidget&) = delete;
TVWidget& operator=(TVWidget&&) = delete;
void on_show() override;
void on_hide() override;
void set_parent_rect(const Rect new_parent_rect) override;
void show_audio_spectrum_view(const bool show);
void paint(Painter& painter) override;
NumberField field_xcorr {
{ 0 * 8, 0 * 16 },
5,
{ 0, 128 },
1,
' '
};
private:
void update_widgets_rect();
const Rect audio_spectrum_view_rect { 0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20 };
static constexpr Dim audio_spectrum_height = 16 * 2 + 20;
static constexpr Dim scale_height = 20;
TVView tv_view { };
ChannelSpectrumFIFO* channel_fifo { nullptr };
AudioSpectrum* audio_spectrum_data { nullptr };
bool audio_spectrum_update { false };
std::unique_ptr<TimeScopeView> audio_spectrum_view { };
int sampling_rate { 0 };
int32_t cursor_position { 0 };
ui::Rect tv_normal_rect { };
ui::Rect tv_reduced_rect { };
MessageHandlerRegistration message_handler_channel_spectrum_config {
Message::ID::ChannelSpectrumConfig,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
this->channel_fifo = message.fifo;
}
};
MessageHandlerRegistration message_handler_audio_spectrum {
Message::ID::AudioSpectrum,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const AudioSpectrumMessage*>(p);
this->audio_spectrum_data = message.data;
this->audio_spectrum_update = true;
}
};
MessageHandlerRegistration message_handler_frame_sync {
Message::ID::DisplayFrameSync,
[this](const Message* const) {
if( this->channel_fifo ) {
ChannelSpectrum channel_spectrum;
while( channel_fifo->out(channel_spectrum) ) {
this->on_channel_spectrum(channel_spectrum);
}
}
if (this->audio_spectrum_update) {
this->audio_spectrum_update = false;
this->on_audio_spectrum();
}
}
};
void on_channel_spectrum(const ChannelSpectrum& spectrum);
void on_audio_spectrum();
};
} /* namespace tv */
} /* namespace ui */
#endif/*__UI_TV_H__*/

View File

@ -34,6 +34,8 @@
#include "ui_adsb_rx.hpp" #include "ui_adsb_rx.hpp"
#include "ui_adsb_tx.hpp" #include "ui_adsb_tx.hpp"
#include "ui_afsk_rx.hpp" #include "ui_afsk_rx.hpp"
#include "ui_btle_rx.hpp"
#include "ui_nrf_rx.hpp"
#include "ui_aprs_tx.hpp" #include "ui_aprs_tx.hpp"
#include "ui_bht_tx.hpp" #include "ui_bht_tx.hpp"
#include "ui_coasterp.hpp" #include "ui_coasterp.hpp"
@ -68,6 +70,7 @@
#include "acars_app.hpp" #include "acars_app.hpp"
#include "ais_app.hpp" #include "ais_app.hpp"
#include "analog_audio_app.hpp" #include "analog_audio_app.hpp"
#include "analog_tv_app.hpp"
#include "capture_app.hpp" #include "capture_app.hpp"
#include "ert_app.hpp" #include "ert_app.hpp"
#include "lge_app.hpp" #include "lge_app.hpp"
@ -348,7 +351,10 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
{ "ACARS", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ACARSAppView>(); }, }, { "ACARS", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ACARSAppView>(); }, },
{ "AIS Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.push<AISAppView>(); } }, { "AIS Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.push<AISAppView>(); } },
{ "AFSK", ui::Color::yellow(), &bitmap_icon_receivers, [&nav](){ nav.push<AFSKRxView>(); } }, { "AFSK", ui::Color::yellow(), &bitmap_icon_receivers, [&nav](){ nav.push<AFSKRxView>(); } },
{ "BTLE", ui::Color::yellow(), &bitmap_icon_btle, [&nav](){ nav.push<BTLERxView>(); } },
{ "NRF", ui::Color::yellow(), &bitmap_icon_nrf, [&nav](){ nav.push<NRFRxView>(); } },
{ "Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav](){ nav.push<AnalogAudioView>(); } }, { "Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav](){ nav.push<AnalogAudioView>(); } },
{ "Analog TV", ui::Color::yellow(), &bitmap_icon_sstv, [&nav](){ nav.push<AnalogTvView>(); } },
{ "ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push<ERTAppView>(); } }, { "ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push<ERTAppView>(); } },
{ "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push<POCSAGAppView>(); } }, { "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push<POCSAGAppView>(); } },
{ "Radiosnde", ui::Color::yellow(), &bitmap_icon_sonde, [&nav](){ nav.push<SondeView>(); } }, { "Radiosnde", ui::Color::yellow(), &bitmap_icon_sonde, [&nav](){ nav.push<SondeView>(); } },

View File

@ -116,6 +116,7 @@ set(CPPSRC
dsp_goertzel.cpp dsp_goertzel.cpp
matched_filter.cpp matched_filter.cpp
spectrum_collector.cpp spectrum_collector.cpp
tv_collector.cpp
stream_input.cpp stream_input.cpp
stream_output.cpp stream_output.cpp
dsp_squelch.cpp dsp_squelch.cpp
@ -311,7 +312,19 @@ set(MODE_CPPSRC
proc_afskrx.cpp proc_afskrx.cpp
) )
DeclareTargets(PAFR afskrx) DeclareTargets(PAFR afskrx)
### NRF RX
set(MODE_CPPSRC
proc_nrfrx.cpp
)
DeclareTargets(PNRR nrfrx)
### BTLE RX
set(MODE_CPPSRC
proc_btlerx.cpp
)
DeclareTargets(PBTR btlerx)
### AIS ### AIS
set(MODE_CPPSRC set(MODE_CPPSRC
@ -326,6 +339,13 @@ set(MODE_CPPSRC
) )
DeclareTargets(PAMA am_audio) DeclareTargets(PAMA am_audio)
### AM TV
set(MODE_CPPSRC
proc_am_tv.cpp
)
DeclareTargets(PAMT am_tv)
### Audio transmit ### Audio transmit
set(MODE_CPPSRC set(MODE_CPPSRC

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2020 Shao
*
* 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_am_tv.hpp"
#include "portapack_shared_memory.hpp"
#include "dsp_fft.hpp"
#include "event_m4.hpp"
#include <cstdint>
void WidebandFMAudio::execute(const buffer_c8_t& buffer) {
if( !configured ) {
return;
}
std::fill(spectrum.begin(), spectrum.end(), 0);
for(size_t i=0; i<spectrum.size(); i++) {
spectrum[i] += buffer.p[i];
}
const buffer_c16_t buffer_c16 {spectrum.data(),spectrum.size(),buffer.sampling_rate};
channel_spectrum.feed(buffer_c16);
int8_t re, im;
int8_t mag;
for (size_t i = 0; i < 128; i++)
{
re = buffer.p[i].real();
im = buffer.p[i].imag();
mag = __builtin_sqrtf((re * re) + (im * im)) ;
const unsigned int v = re + 127.0f; //timescope
audio_spectrum.db[i] = std::max(0U, std::min(255U, v));
}
AudioSpectrumMessage message { &audio_spectrum };
shared_memory.application_queue.push(message);
}
void WidebandFMAudio::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::WFMConfigure:
configure(*reinterpret_cast<const WFMConfigureMessage*>(message));
break;
default:
break;
}
}
void WidebandFMAudio::configure(const WFMConfigureMessage& message) {
configured = true;
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<WidebandFMAudio>() };
event_dispatcher.run();
return 0;
}

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2020 Shao
*
* 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_AM_TV_H__
#define __PROC_AM_TV_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "dsp_types.hpp"
#include "dsp_decimate.hpp"
#include "dsp_demodulate.hpp"
#include "block_decimator.hpp"
#include "tv_collector.hpp"
class WidebandFMAudio : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 2000000;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
AudioSpectrum audio_spectrum { };
TvCollector channel_spectrum { };
std::array<complex16_t, 256> spectrum { };
bool configured { false };
void configure(const WFMConfigureMessage& message);
};
#endif/*__PROC_AM_TV_H__*/

View File

@ -0,0 +1,339 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2020 Shao
*
* 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_btlerx.hpp"
#include "portapack_shared_memory.hpp"
#include "event_m4.hpp"
void BTLERxProcessor::execute(const buffer_c8_t& buffer) {
if (!configured) return;
// FM demodulation
/*const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
const auto channel = decim_1.execute(decim_0_out, dst_buffer);
feed_channel_stats(channel);
auto audio_oversampled = demod.execute(channel, work_audio_buffer);*/
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
feed_channel_stats(decim_0_out);
auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer);
/*std::fill(spectrum.begin(), spectrum.end(), 0);
for(size_t i=0; i<spectrum.size(); i++) {
spectrum[i] += buffer.p[i];
}
const buffer_c16_t buffer_c16 {spectrum.data(),spectrum.size(),buffer.sampling_rate};
feed_channel_stats(buffer_c16);
auto audio_oversampled = demod.execute(buffer_c16, work_audio_buffer);*/
// Audio signal processing
for (size_t c = 0; c < audio_oversampled.count; c++) {
int result;
/*const int32_t sample_int = audio_oversampled.p[c] * 32768.0f;
int32_t current_sample = __SSAT(sample_int, 16);
current_sample /= 128;*/
int32_t current_sample = audio_oversampled.p[c]; //if I directly use this, some results can pass crc but not correct.
rb_head++;
rb_head=(rb_head)%RB_SIZE;
rb_buf[rb_head] = current_sample;
skipSamples = skipSamples - 1;
if (skipSamples<1)
{
int32_t threshold_tmp=0;
for (int c=0;c<8;c++)
{
threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head+c)%RB_SIZE];
}
g_threshold = (int32_t)threshold_tmp/8;
int transitions=0;
if (rb_buf[(rb_head+9)%RB_SIZE] > g_threshold)
{
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + c)%RB_SIZE] > rb_buf[(rb_head + c + 1)%RB_SIZE])
transitions = transitions + 1;
}
}
else
{
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + c)%RB_SIZE] < rb_buf[(rb_head + c + 1)%RB_SIZE])
transitions = transitions + 1;
}
}
bool packet_detected=false;
//if ( transitions==4 && abs(g_threshold)<15500)
if ( transitions==4)
{
uint8_t packet_data[500];
int packet_length;
uint32_t packet_crc;
uint32_t calced_crc;
uint64_t packet_addr_l;
uint32_t result;
uint8_t crc[3];
uint8_t packet_header_arr[2];
packet_addr_l=0;
for (int i=0;i<4;i++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + (i+1)*8 + c)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
uint8_t byte_temp = (uint8_t) (((byte * 0x0802LU & 0x22110LU) | (byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
packet_addr_l|=((uint64_t)byte_temp)<<(8*i);
}
channel_number = 38;
for (int t=0;t<2;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + 5*8+t*8 + c)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
packet_header_arr[t] = byte;
}
uint8_t byte_temp2 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
uint8_t lfsr_1 = byte_temp2 | 2;
int header_length = 2;
int header_counter = 0;
while(header_length--)
{
for(uint8_t i = 0x80; i; i >>= 1)
{
if(lfsr_1 & 0x80)
{
lfsr_1 ^= 0x11;
(packet_header_arr[header_counter]) ^= i;
}
lfsr_1 <<= 1;
}
header_counter = header_counter + 1;
}
if (packet_addr_l==0x8E89BED6)
{
uint8_t byte_temp3 = (uint8_t) (((packet_header_arr[1] * 0x0802LU & 0x22110LU) | (packet_header_arr[1] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
packet_length=byte_temp3&0x3F;
}
else
{
packet_length=0;
}
for (int t=0;t<packet_length+2+3;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + 5*8+t*8 + c)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
packet_data[t] = byte;
}
uint8_t byte_temp4 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
uint8_t lfsr_2 = byte_temp4 | 2;
int pdu_crc_length = packet_length+2+3;
int pdu_crc_counter = 0;
while(pdu_crc_length--)
{
for(uint8_t i = 0x80; i; i >>= 1)
{
if(lfsr_2 & 0x80)
{
lfsr_2 ^= 0x11;
(packet_data[pdu_crc_counter]) ^= i;
}
lfsr_2 <<= 1;
}
pdu_crc_counter = pdu_crc_counter + 1;
}
if (packet_addr_l==0x8E89BED6)
{
crc[0]=crc[1]=crc[2]=0x55;
}
else
{
crc[0]=crc[1]=crc[2]=0;
}
uint8_t v, t, d, crc_length;
uint32_t crc_result=0;
crc_length = packet_length + 2;
int counter = 0;
while(crc_length--)
{
uint8_t byte_temp5 = (uint8_t) (((packet_data[counter] * 0x0802LU & 0x22110LU) | (packet_data[counter] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
d = byte_temp5;
for(v = 0; v < 8; v++, d >>= 1)
{
t = crc[0] >> 7;
crc[0] <<= 1;
if(crc[1] & 0x80) crc[0] |= 1;
crc[1] <<= 1;
if(crc[2] & 0x80) crc[1] |= 1;
crc[2] <<= 1;
if(t != (d & 1))
{
crc[2] ^= 0x5B;
crc[1] ^= 0x06;
}
}
counter = counter + 1;
}
for (v=0;v<3;v++) crc_result=(crc_result<<8)|crc[v];
calced_crc = crc_result;
packet_crc=0;
for (int c=0;c<3;c++) packet_crc=(packet_crc<<8)|packet_data[packet_length+2+c];
if (packet_addr_l==0x8E89BED6)
//if (packet_crc==calced_crc)
{
uint8_t mac_data[6];
int counter = 0;
for (int i = 7; i >= 2; i--)
{
uint8_t byte_temp6 = (uint8_t) (((packet_data[i] * 0x0802LU & 0x22110LU) | (packet_data[i] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16);
//result = byte_temp6;
mac_data[counter] = byte_temp6;
counter = counter + 1;
}
data_message.is_data = false;
data_message.value = 'A';
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[0];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[1];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[2];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[3];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[4];
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = mac_data[5];
shared_memory.application_queue.push(data_message);
data_message.is_data = false;
data_message.value = 'B';
shared_memory.application_queue.push(data_message);
packet_detected = true;
}
else
packet_detected = false;
}
if (packet_detected)
{
skipSamples=20;
}
}
}
}
void BTLERxProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::BTLERxConfigure)
configure(*reinterpret_cast<const BTLERxConfigureMessage*>(message));
}
void BTLERxProcessor::configure(const BTLERxConfigureMessage& message) {
decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
demod.configure(audio_fs, 5000);
configured = true;
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<BTLERxProcessor>() };
event_dispatcher.run();
return 0;
}

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2020 Shao
*
* 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_BTLERX_H__
#define __PROC_BTLERX_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "dsp_decimate.hpp"
#include "dsp_demodulate.hpp"
#include "audio_output.hpp"
#include "fifo.hpp"
#include "message.hpp"
class BTLERxProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 4000000;
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
std::array<complex16_t, 512> spectrum { };
const buffer_c16_t spectrum_buffer {
spectrum.data(),
spectrum.size()
};
const buffer_s16_t work_audio_buffer {
(int16_t*)dst.data(),
sizeof(dst) / sizeof(int16_t)
};
// Array size ok down to 375 bauds (24000 / 375)
std::array<int32_t, 64> delay_line { 0 };
std::array<int16_t, 1000> rb_buf { 0 };
/*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };*/
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { };
dsp::decimate::FIRC16xR16x16Decim2 decim_1 { };
dsp::demodulate::FM demod { };
int rb_head {-1};
int32_t g_threshold {0};
uint8_t channel_number {38};
int skipSamples {1000};
int RB_SIZE {1000};
bool configured { false };
void configure(const BTLERxConfigureMessage& message);
AFSKDataMessage data_message { false, 0 };
};
#endif/*__PROC_BTLERX_H__*/

View File

@ -0,0 +1,286 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2020 Shao
*
* 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_nrfrx.hpp"
#include "portapack_shared_memory.hpp"
#include "event_m4.hpp"
void NRFRxProcessor::execute(const buffer_c8_t& buffer) {
if (!configured) return;
// FM demodulation
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
feed_channel_stats(decim_0_out);
auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer);
// Audio signal processing
for (size_t c = 0; c < audio_oversampled.count; c++) {
int result;
int g_srate = 4; //4 for 250KPS
//int g_srate = 1; //1 for 1MPS, not working yet
int32_t current_sample = audio_oversampled.p[c]; //if I directly use this, some results can pass crc but not correct.
rb_head++;
rb_head=(rb_head)%RB_SIZE;
rb_buf[rb_head] = current_sample;
skipSamples = skipSamples - 1;
if (skipSamples<1)
{
int32_t threshold_tmp=0;
for (int c=0;c<8*g_srate;c++)
{
threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head+c)%RB_SIZE];
}
g_threshold = (int32_t)threshold_tmp/(8*g_srate);
int transitions=0;
if (rb_buf[(rb_head + 9*g_srate)%RB_SIZE] > g_threshold)
{
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] > rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE])
transitions = transitions + 1;
}
}
else
{
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] < rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE])
transitions = transitions + 1;
}
}
bool packet_detected=false;
//if ( transitions==4 && abs(g_threshold)<15500)
if ( transitions==4 && abs(g_threshold)<15500)
{
int packet_length = 0;
uint8_t tmp_buf[10];
uint8_t packet_data[500];
uint8_t packet_packed[50];
uint16_t pcf;
uint32_t packet_crc;
uint32_t calced_crc;
uint64_t packet_addr_l;
/* extract address */
packet_addr_l=0;
for (int t=0;t<5;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head+(1*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
tmp_buf[t]=byte;
}
for (int t=0;t<5;t++) packet_addr_l|=((uint64_t)tmp_buf[t])<<(4-t)*8;
//channel_number = 26;
/* extract pcf */
for (int t=0;t<2;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head+(6*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
tmp_buf[t]=byte;
}
pcf = tmp_buf[0]<<8 | tmp_buf[1];
pcf >>=7;
/* extract packet length, avoid excessive length packets */
if(packet_length == 0)
packet_length=(int)pcf>>3;
if (packet_length>32)
packet_detected = false;
/* extract data */
for (int t=0;t<packet_length;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head+(6*8+9+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
packet_data[t]=byte;
}
/* Prepare packed bit stream for CRC calculation */
uint64_t packet_header=packet_addr_l;
packet_header<<=9;
packet_header|=pcf;
for (int c=0;c<7;c++){
packet_packed[c]=(packet_header>>((6-c)*8))&0xFF;
}
for (int c=0;c<packet_length;c++){
packet_packed[c+7]=packet_data[c];
}
/* calculate packet crc */
const uint8_t* data = packet_packed;
size_t data_len = 7+packet_length;
bool bit;
uint8_t cc;
uint_fast16_t crc=0x3C18;
while (data_len--) {
cc = *data++;
for (uint8_t i = 0x80; i > 0; i >>= 1)
{
bit = crc & 0x8000;
if (cc & i)
{
bit = !bit;
}
crc <<= 1;
if (bit)
{
crc ^= 0x1021;
}
}
crc &= 0xffff;
}
calced_crc = (uint16_t)(crc & 0xffff);
/* extract crc */
for (int t=0;t<2;t++)
{
bool current_bit;
uint8_t byte=0;
for (int c=0;c<8;c++)
{
if (rb_buf[(rb_head+((6+packet_length)*8+9+t*8+c)*g_srate)%RB_SIZE] > g_threshold)
current_bit = true;
else
current_bit = false;
byte |= current_bit << (7-c);
}
tmp_buf[t]=byte;
}
packet_crc = tmp_buf[0]<<8 | tmp_buf[1];
/* NRF24L01+ packet found, dump information */
//if (packet_addr_l==0xE7E7E7E7)
if (packet_crc==calced_crc)
{
data_message.is_data = false;
data_message.value = 'A';
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = packet_addr_l;
shared_memory.application_queue.push(data_message);
for (int c=0;c<7;c++)
{
data_message.is_data = true;
data_message.value = packet_addr_l >> 8;
shared_memory.application_queue.push(data_message);
}
/*data_message.is_data = true;
data_message.value = packet_addr_l;
shared_memory.application_queue.push(data_message);
data_message.is_data = true;
data_message.value = packet_addr_l >> 8;
shared_memory.application_queue.push(data_message);*/
data_message.is_data = false;
data_message.value = 'B';
shared_memory.application_queue.push(data_message);
for (int c=0;c<packet_length;c++)
{
data_message.is_data = true;
data_message.value = packet_data[c];
shared_memory.application_queue.push(data_message);
}
data_message.is_data = false;
data_message.value = 'C';
shared_memory.application_queue.push(data_message);
packet_detected = true;
}
else
packet_detected = false;
}
if (packet_detected)
{
skipSamples=20;
}
}
}
}
void NRFRxProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::NRFRxConfigure)
configure(*reinterpret_cast<const NRFRxConfigureMessage*>(message));
}
void NRFRxProcessor::configure(const NRFRxConfigureMessage& message) {
decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
demod.configure(audio_fs, 5000);
configured = true;
}
int main() {
EventDispatcher event_dispatcher { std::make_unique<NRFRxProcessor>() };
event_dispatcher.run();
return 0;
}

View File

@ -0,0 +1,96 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2020 Shao
*
* 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_NRFRX_H__
#define __PROC_NRFRX_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "dsp_decimate.hpp"
#include "dsp_demodulate.hpp"
#include "audio_output.hpp"
#include "fifo.hpp"
#include "message.hpp"
class NRFRxProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 4000000;
static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2;
BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive };
RSSIThread rssi_thread { NORMALPRIO + 10 };
std::array<complex16_t, 512> dst { };
const buffer_c16_t dst_buffer {
dst.data(),
dst.size()
};
std::array<complex16_t, 512> spectrum { };
const buffer_c16_t spectrum_buffer {
spectrum.data(),
spectrum.size()
};
const buffer_s16_t work_audio_buffer {
(int16_t*)dst.data(),
sizeof(dst) / sizeof(int16_t)
};
// Array size ok down to 375 bauds (24000 / 375)
std::array<int32_t, 64> delay_line { 0 };
std::array<int16_t, 1000> rb_buf { 0 };
/*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
dsp::decimate::FIRAndDecimateComplex channel_filter { };*/
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { };
dsp::decimate::FIRC16xR16x16Decim2 decim_1 { };
dsp::demodulate::FM demod { };
int rb_head {-1};
int32_t g_threshold {0};
//uint8_t g_srate {8};
uint8_t channel_number {38};
int skipSamples {1000};
int RB_SIZE {1000};
bool configured { false };
void configure(const NRFRxConfigureMessage& message);
AFSKDataMessage data_message { false, 0 };
};
#endif/*__PROC_NRFRX_H__*/

View File

@ -0,0 +1,122 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2020 Shao
*
* 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 "tv_collector.hpp"
#include "dsp_fft.hpp"
#include "utility.hpp"
#include "event_m4.hpp"
#include "portapack_shared_memory.hpp"
#include "event_m4.hpp"
#include <algorithm>
void TvCollector::on_message(const Message* const message) {
switch(message->id) {
case Message::ID::UpdateSpectrum:
update();
break;
case Message::ID::SpectrumStreamingConfig:
set_state(*reinterpret_cast<const SpectrumStreamingConfigMessage*>(message));
break;
default:
break;
}
}
void TvCollector::set_state(const SpectrumStreamingConfigMessage& message) {
if( message.mode == SpectrumStreamingConfigMessage::Mode::Running ) {
start();
} else {
stop();
}
}
void TvCollector::start() {
streaming = true;
ChannelSpectrumConfigMessage message { &fifo };
shared_memory.application_queue.push(message);
}
void TvCollector::stop() {
streaming = false;
fifo.reset_in();
}
void TvCollector::set_decimation_factor(
const size_t decimation_factor
) {
channel_spectrum_decimator.set_factor(decimation_factor);
}
/* TODO: Refactor to register task with idle thread?
* It's sad that the idle thread has to call all the way back here just to
* perform the deferred task on the buffer of data we prepared.
*/
void TvCollector::feed(
const buffer_c16_t& channel
) {
// Called from baseband processing thread.
channel_spectrum_decimator.feed(channel,[this](const buffer_c16_t& data) {this->post_message(data);});
}
void TvCollector::post_message(const buffer_c16_t& data) {
// Called from baseband processing thread.
float re, im;
float mag;
float max;
if( streaming && !channel_spectrum_request_update ) {
for(size_t i=0; i<256; i++)
{
const auto s = data.p[i];
re = (float)(data.p[i].real());
im = (float)(data.p[i].imag());
mag = __builtin_sqrtf((re * re) + (im * im)) ;
channel_spectrum[i] = {mag, mag};
}
channel_spectrum_sampling_rate = data.sampling_rate;
channel_spectrum_request_update = true;
EventDispatcher::events_flag(EVT_MASK_SPECTRUM);
}
}
void TvCollector::update() {
// Called from idle thread (after EVT_MASK_SPECTRUM is flagged)
if( streaming && channel_spectrum_request_update ) {
ChannelSpectrum spectrum;
spectrum.sampling_rate = channel_spectrum_sampling_rate;
for(size_t i=0; i<spectrum.db.size(); i++) {
const auto corrected_sample = channel_spectrum[i].real();
const unsigned int v = corrected_sample + 127.0f;
spectrum.db[i] = std::max(0U, std::min(255U, v));
}
fifo.in(spectrum);
}
channel_spectrum_request_update = false;
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2020 Shao
*
* 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 __TV_COLLECTOR_H__
#define __TV_COLLECTOR_H__
#include "dsp_types.hpp"
#include "complex.hpp"
#include "block_decimator.hpp"
#include <cstdint>
#include <array>
#include "message.hpp"
class TvCollector {
public:
void on_message(const Message* const message);
void set_decimation_factor(const size_t decimation_factor);
void feed(
const buffer_c16_t& channel
);
private:
BlockDecimator<complex16_t, 256> channel_spectrum_decimator { 1 };
ChannelSpectrum fifo_data[1 << ChannelSpectrumConfigMessage::fifo_k] { };
ChannelSpectrumFIFO fifo { fifo_data, ChannelSpectrumConfigMessage::fifo_k };
volatile bool channel_spectrum_request_update { false };
bool streaming { false };
std::array<std::complex<float>, 256> channel_spectrum { };
uint32_t channel_spectrum_sampling_rate { 0 };
void post_message(const buffer_c16_t& data);
void set_state(const SpectrumStreamingConfigMessage& message);
void start();
void stop();
void update();
};
#endif/*__TV_COLLECTOR_H__*/

View File

@ -78,6 +78,8 @@ public:
AFSKRxConfigure = 22, AFSKRxConfigure = 22,
StatusRefresh = 23, StatusRefresh = 23,
SamplerateConfig = 24, SamplerateConfig = 24,
BTLERxConfigure = 25,
NRFRxConfigure = 26,
TXProgress = 30, TXProgress = 30,
Retune = 31, Retune = 31,
@ -722,6 +724,46 @@ public:
const bool trigger_word; const bool trigger_word;
}; };
class BTLERxConfigureMessage : public Message {
public:
constexpr BTLERxConfigureMessage(
const uint32_t baudrate,
const uint32_t word_length,
const uint32_t trigger_value,
const bool trigger_word
) : Message { ID::BTLERxConfigure },
baudrate(baudrate),
word_length(word_length),
trigger_value(trigger_value),
trigger_word(trigger_word)
{
}
const uint32_t baudrate;
const uint32_t word_length;
const uint32_t trigger_value;
const bool trigger_word;
};
class NRFRxConfigureMessage : public Message {
public:
constexpr NRFRxConfigureMessage(
const uint32_t baudrate,
const uint32_t word_length,
const uint32_t trigger_value,
const bool trigger_word
) : Message { ID::NRFRxConfigure },
baudrate(baudrate),
word_length(word_length),
trigger_value(trigger_value),
trigger_word(trigger_word)
{
}
const uint32_t baudrate;
const uint32_t word_length;
const uint32_t trigger_value;
const bool trigger_word;
};
class PitchRSSIConfigureMessage : public Message { class PitchRSSIConfigureMessage : public Message {
public: public:
constexpr PitchRSSIConfigureMessage( constexpr PitchRSSIConfigureMessage(

View File

@ -68,8 +68,11 @@ private:
constexpr image_tag_t image_tag_acars { 'P', 'A', 'C', 'A' }; constexpr image_tag_t image_tag_acars { 'P', 'A', 'C', 'A' };
constexpr image_tag_t image_tag_adsb_rx { 'P', 'A', 'D', 'R' }; constexpr image_tag_t image_tag_adsb_rx { 'P', 'A', 'D', 'R' };
constexpr image_tag_t image_tag_afsk_rx { 'P', 'A', 'F', 'R' }; constexpr image_tag_t image_tag_afsk_rx { 'P', 'A', 'F', 'R' };
constexpr image_tag_t image_tag_btle_rx { 'P', 'B', 'T', 'R' };
constexpr image_tag_t image_tag_nrf_rx { 'P', 'N', 'R', 'R' };
constexpr image_tag_t image_tag_ais { 'P', 'A', 'I', 'S' }; constexpr image_tag_t image_tag_ais { 'P', 'A', 'I', 'S' };
constexpr image_tag_t image_tag_am_audio { 'P', 'A', 'M', 'A' }; constexpr image_tag_t image_tag_am_audio { 'P', 'A', 'M', 'A' };
constexpr image_tag_t image_tag_am_tv { 'P', 'A', 'M', 'T' };
constexpr image_tag_t image_tag_capture { 'P', 'C', 'A', 'P' }; constexpr image_tag_t image_tag_capture { 'P', 'C', 'A', 'P' };
constexpr image_tag_t image_tag_ert { 'P', 'E', 'R', 'T' }; constexpr image_tag_t image_tag_ert { 'P', 'E', 'R', 'T' };
constexpr image_tag_t image_tag_nfm_audio { 'P', 'N', 'F', 'M' }; constexpr image_tag_t image_tag_nfm_audio { 'P', 'N', 'F', 'M' };