mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-08-22 07:29:03 +00:00
Compare commits
48 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c1bf2620c7 | |||
| 8383363e74 | |||
| 6e34343bde | |||
| 01e4ff65a2 | |||
| 9e40e38f07 | |||
| 181624daf1 | |||
| f0614c882b | |||
| 14f42d1fe8 | |||
| 999f9e2ded | |||
| 61dc8a0225 | |||
| 0b2d5f75cc | |||
| 3c489e1a81 | |||
| 910fd82051 | |||
| 866e12fbc5 | |||
| ccd71d9593 | |||
| e9b9ba4602 | |||
| 986e37a3c9 | |||
| 2ac36d2d0b | |||
| 3b79549129 | |||
| 160a778104 | |||
| e496f8ecc2 | |||
| 9d8132978f | |||
| 73c29f666f | |||
| d895c9fa91 | |||
| b5e66387c3 | |||
| 1fbfdbccf8 | |||
| 2648f18b12 | |||
| 908c0e2cd5 | |||
| 4f3530db5e | |||
| 73137891c4 | |||
| c28087ac44 | |||
| 9e9dd3a521 | |||
| 3bf689f339 | |||
| 86c326ef12 | |||
| 5ae97d47f1 | |||
| 762512b7dc | |||
| 9c848a6215 | |||
| 9c86e15ddb | |||
| 57ccdb0044 | |||
| 1172175cc8 | |||
| 9ed8ad5903 | |||
| e4da8b979e | |||
| 4b83fd8a40 | |||
| cd80da58d8 | |||
| a1f7010e08 | |||
| 3499d2fa75 | |||
| 948e039574 | |||
| 27dc37713b |
@@ -0,0 +1,48 @@
|
||||
name: Bug report
|
||||
description: File a bug reports regarding the firmware.
|
||||
labels: ['bug']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out an issue, this template is meant for any issues related to the Mayhem firmware.
|
||||
Please try the latest nightly release before submitting this. You can find the latest nightly version here: https://github.com/portapack-mayhem/mayhem-firmware/releases
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Describe the bug.
|
||||
description: "A clear and concise description of what the bug is."
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: Reproduction
|
||||
description: "How can this bug be reproduced?"
|
||||
placeholder: |
|
||||
1. Switch on...
|
||||
2. Press button '....'
|
||||
3. Wait for the end of the universe
|
||||
4. It burns
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected behavior
|
||||
description: "A clear and concise description of what you expected to happen"
|
||||
placeholder: |
|
||||
1. Generates file on...
|
||||
2. I get a cheeseburger...
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: target
|
||||
attributes:
|
||||
label: Environment/versions
|
||||
description: Specify extra details about versions and environments affected
|
||||
- type: textarea
|
||||
id: anything-else
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: Let us know if you have anything else to share.
|
||||
@@ -0,0 +1,24 @@
|
||||
name: Feature Request
|
||||
description: For feature requests regarding the firmware.
|
||||
labels: ['feature request']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thank you for taking the time to fill out an issue, this template is meant for any feature suggestions.
|
||||
Please try the latest nightly release before submitting this, make sure it has not been implemented already. You can find the latest nightly version here: https://github.com/portapack-mayhem/mayhem-firmware/releases
|
||||
- type: textarea
|
||||
id: proposal
|
||||
attributes:
|
||||
label: "Description of the feature you're suggesting."
|
||||
description: |
|
||||
Please describe your feature request in as many details as possible.
|
||||
- Describe what it should do.
|
||||
- Note whether it is to extend existing functionality or introduce new functionality.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: anything-else
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: Let us know if you have anything else to share.
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve the software
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
----
|
||||
|
||||
(Please try the latest nightly release before submitting this. You can find the latest nightly version here: https://github.com/portapack-mayhem/mayhem-firmware/releases)
|
||||
|
||||
----
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Tap on '....'
|
||||
3.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Affected versions**
|
||||
Please write any difference related with the Expected behavior, on the following versions:
|
||||
* Latest Stable release:
|
||||
* Latest Nightly release:
|
||||
* Previous working release:
|
||||
|
||||
**Additional**
|
||||
If the bug is difficult to explain, additionally to the text please include images and videos.
|
||||
@@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Need more help?
|
||||
url: https://discord.com/invite/tuwVMv3
|
||||
about: For any question that does not fit as an issue, check our discord.
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: enhancement
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen. Remember that adding stuff is always possible, but time is a limited resource for everyone. Check the wiki for more information how to compile the firmware and try to explore modifying the code yourself.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional**
|
||||
If the suggestion is difficult to explain, additionally to the text please include images and videos.
|
||||
+7
-1
@@ -68,9 +68,15 @@ CMakeFiles/
|
||||
.DS_Store
|
||||
/firmware/CMakeCache.txt
|
||||
|
||||
# Python env/ venv
|
||||
# Python env/ venv and cache
|
||||
env/
|
||||
venv/
|
||||
**/__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Other
|
||||
*.bak
|
||||
|
||||
# generated bitmap arr file
|
||||
# TODO: generate bitmap during build, since we use python during build anyway, lemme know if this is a bad idea @zxkmm
|
||||
/firmware/tools/bitmap.hpp
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
> [!IMPORTANT]
|
||||
> this repository **has just been moved** from my personal GitHub [eried/portapack-mayhem](https://github.com/eried/portapack-mayhem) to an organization: [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware). Please keep this in mind to **update your links** accordingly.
|
||||
> [!WARNING]
|
||||
> __IF YOU'VE PAID FOR MAYHEM OR ANY PREPACKAGED PACKAGES, YOU'RE BEING SCAMMED.__
|
||||
> The only legitimate link leading to our repositories is the organization [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware).
|
||||
|
||||
# PortaPack Mayhem
|
||||
|
||||
@@ -13,11 +14,11 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
|
||||
|
||||
# What is this?
|
||||
|
||||
If you are new to *HackRF+PortaPack+Mayhem*, check this video:
|
||||
If you are new to *HackRF+PortaPack+Mayhem*, check these:
|
||||
|
||||
[](https://www.youtube.com/watch?v=H-bqdWfbhpg)
|
||||
[<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="512">](https://grabify.link/C0J6ZR)
|
||||
|
||||
For people familiar with the [Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware), this one might be interesting:<br>[What is the HackRF One Portapack H2+?](https://www.youtube.com/watch?v=alrFbY5vxt4)
|
||||
[<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="254">](https://grabify.link/5MU2VH) [<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="254">](https://grabify.link/9UZGEW)
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "string_format.hpp"
|
||||
#include "ui_freqman.hpp"
|
||||
#include "utility.hpp"
|
||||
#include "radio.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace tonekey;
|
||||
@@ -112,10 +113,14 @@ SPECOptionsView::SPECOptionsView(
|
||||
: View{parent_rect} {
|
||||
set_style(style);
|
||||
|
||||
add_children({&label_config,
|
||||
&options_config,
|
||||
&text_speed,
|
||||
&field_speed});
|
||||
add_children({
|
||||
&label_config,
|
||||
&options_config,
|
||||
&text_speed,
|
||||
&field_speed,
|
||||
&text_rx_cal,
|
||||
&field_rx_iq_phase_cal,
|
||||
});
|
||||
|
||||
options_config.set_selected_index(view->get_spec_bw_index());
|
||||
options_config.on_change = [this, view](size_t n, OptionsField::value_t bw) {
|
||||
@@ -126,6 +131,14 @@ SPECOptionsView::SPECOptionsView(
|
||||
field_speed.on_change = [this, view](int32_t v) {
|
||||
view->set_spec_trigger(v);
|
||||
};
|
||||
|
||||
field_rx_iq_phase_cal.set_range(0, hackrf_r9 ? 63 : 31); // max2839 has 6 bits [0..63], max2837 has 5 bits [0..31]
|
||||
field_rx_iq_phase_cal.set_value(view->get_spec_iq_phase_calibration_value()); // using accessor function of AnalogAudioView to read iq_phase_calibration_value from rx_audio.ini
|
||||
field_rx_iq_phase_cal.on_change = [this, view](int32_t v) {
|
||||
view->set_spec_iq_phase_calibration_value(v); // using accessor function of AnalogAudioView to write inside SPEC submenu, register value to max283x and save it to rx_audio.ini
|
||||
};
|
||||
|
||||
view->set_spec_iq_phase_calibration_value(view->get_spec_iq_phase_calibration_value()); // initialize iq_phase_calibration in radio
|
||||
}
|
||||
|
||||
/* AnalogAudioView *******************************************************/
|
||||
@@ -213,6 +226,15 @@ void AnalogAudioView::set_spec_bw(size_t index, uint32_t bw) {
|
||||
receiver_model.set_baseband_bandwidth(bw / 2);
|
||||
}
|
||||
|
||||
uint8_t AnalogAudioView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read & write real iq_phase_calibration_value
|
||||
return iq_phase_calibration_value;
|
||||
}
|
||||
|
||||
void AnalogAudioView::set_spec_iq_phase_calibration_value(uint8_t cal_value) { // define accessor functions
|
||||
iq_phase_calibration_value = cal_value;
|
||||
radio::set_rx_max283x_iq_phase_calibration(iq_phase_calibration_value);
|
||||
}
|
||||
|
||||
uint16_t AnalogAudioView::get_spec_trigger() {
|
||||
return spec_trigger;
|
||||
}
|
||||
|
||||
@@ -133,6 +133,16 @@ class SPECOptionsView : public View {
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
Text text_rx_cal{
|
||||
{19 * 8, 0 * 16, 11 * 8, 1 * 16}, // 18 (x col.) x char_size, 12 (length) x 8 blanking space to delete previous chars.
|
||||
"Rx_IQ_CAL "};
|
||||
NumberField field_rx_iq_phase_cal{
|
||||
{28 * 8, 0 * 16},
|
||||
2,
|
||||
{0, 63}, // 5 or 6 bits IQ CAL phase adjustment (range updated later)
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
};
|
||||
|
||||
class AnalogAudioView : public View {
|
||||
@@ -152,14 +162,22 @@ class AnalogAudioView : public View {
|
||||
uint16_t get_spec_trigger();
|
||||
void set_spec_trigger(uint16_t trigger);
|
||||
|
||||
uint8_t get_spec_iq_phase_calibration_value();
|
||||
void set_spec_iq_phase_calibration_value(uint8_t cal_value);
|
||||
|
||||
private:
|
||||
static constexpr ui::Dim header_height = 3 * 16;
|
||||
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{};
|
||||
uint8_t iq_phase_calibration_value{15}; // initial default RX IQ phase calibration value , used for both max2837 & max2839
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_audio", app_settings::Mode::RX,
|
||||
app_settings::Options::UseGlobalTargetFrequency};
|
||||
"rx_audio",
|
||||
app_settings::Mode::RX,
|
||||
app_settings::Options::UseGlobalTargetFrequency,
|
||||
{
|
||||
{"iq_phase_calibration"sv, &iq_phase_calibration_value}, // we are saving and restoring that CAL from Settings.
|
||||
}};
|
||||
|
||||
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};
|
||||
|
||||
@@ -13,7 +13,7 @@ AboutView::AboutView(NavigationView& nav) {
|
||||
}
|
||||
|
||||
void AboutView::update() {
|
||||
if (++timer > 200) {
|
||||
if (++timer > 400) {
|
||||
timer = 0;
|
||||
|
||||
switch (++frame) {
|
||||
@@ -23,7 +23,7 @@ void AboutView::update() {
|
||||
console.writeln(STR_COLOR_DARK_YELLOW "Mayhem:");
|
||||
console.writeln("eried,euquiq,gregoryfenton");
|
||||
console.writeln("johnelder,jwetzell,nnemanjan00");
|
||||
console.writeln("N0vaPixel,klockee,GullCode");
|
||||
console.writeln("N0vaPixel,klockee,gullradriel");
|
||||
console.writeln("jamesshao8,ITAxReal,rascafr");
|
||||
console.writeln("mcules,dqs105,strijar");
|
||||
console.writeln("zhang00963,RedFox-Fr,aldude999");
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
* Copyright (C) 2024 u-foka
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -445,14 +446,19 @@ void DebugControlsView::focus() {
|
||||
|
||||
/* DebugPeripheralsMenuView **********************************************/
|
||||
|
||||
DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav) {
|
||||
DebugPeripheralsMenuView::DebugPeripheralsMenuView(NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
|
||||
void DebugPeripheralsMenuView::on_populate() {
|
||||
const char* max283x = hackrf_r9 ? "MAX2839" : "MAX2837";
|
||||
const char* si5351x = hackrf_r9 ? "Si5351A" : "Si5351C";
|
||||
add_items({
|
||||
{"RFFC5072", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push<RegistersView>("RFFC5072", RegistersWidgetConfig{CT_RFFC5072, 31, 31, 16}); }},
|
||||
{max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, max283x]() { nav.push<RegistersView>(max283x, RegistersWidgetConfig{CT_MAX283X, 32, 32, 10}); }},
|
||||
{si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav, si5351x]() { nav.push<RegistersView>(si5351x, RegistersWidgetConfig{CT_SI5351, 188, 96, 8}); }},
|
||||
{audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [&nav]() { nav.push<RegistersView>(audio::debug::codec_name(), RegistersWidgetConfig{CT_AUDIO, audio::debug::reg_count(), audio::debug::reg_count(), audio::debug::reg_bits()}); }},
|
||||
{"RFFC5072", ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>("RFFC5072", RegistersWidgetConfig{CT_RFFC5072, 31, 31, 16}); }},
|
||||
{max283x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [this, max283x]() { nav_.push<RegistersView>(max283x, RegistersWidgetConfig{CT_MAX283X, 32, 32, 10}); }},
|
||||
{si5351x, ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [this, si5351x]() { nav_.push<RegistersView>(si5351x, RegistersWidgetConfig{CT_SI5351, 188, 96, 8}); }},
|
||||
{audio::debug::codec_name(), ui::Color::dark_cyan(), &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>(audio::debug::codec_name(), RegistersWidgetConfig{CT_AUDIO, audio::debug::reg_count(), audio::debug::reg_count(), audio::debug::reg_bits()}); }},
|
||||
});
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
@@ -468,32 +474,38 @@ DebugReboot::DebugReboot(NavigationView& nav) {
|
||||
__WFE();
|
||||
}
|
||||
|
||||
void DebugReboot::on_populate() {
|
||||
}
|
||||
|
||||
/* DebugMenuView *********************************************************/
|
||||
|
||||
DebugMenuView::DebugMenuView(NavigationView& nav) {
|
||||
DebugMenuView::DebugMenuView(NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
|
||||
void DebugMenuView::on_populate() {
|
||||
if (portapack::persistent_memory::show_gui_return_icon()) {
|
||||
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
|
||||
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [this]() { nav_.pop(); }}});
|
||||
}
|
||||
add_items({
|
||||
{"Buttons Test", ui::Color::dark_cyan(), &bitmap_icon_controls, [&nav]() { nav.push<DebugControlsView>(); }},
|
||||
{"Debug Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { portapack::persistent_memory::debug_dump(); }},
|
||||
{"M0 Stack Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { stack_dump(); }},
|
||||
{"Memory Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<DebugMemoryDumpView>(); }},
|
||||
{"Memory Usage", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<DebugMemoryView>(); }},
|
||||
{"Peripherals", ui::Color::dark_cyan(), &bitmap_icon_peripherals, [&nav]() { nav.push<DebugPeripheralsMenuView>(); }},
|
||||
{"Pers. Memory", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<DebugPmemView>(); }},
|
||||
//{ "Radio State", ui::Color::white(), nullptr, [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{"Reboot", ui::Color::dark_cyan(), &bitmap_icon_setup, [&nav]() { nav.push<DebugReboot>(); }},
|
||||
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [&nav]() { nav.push<SDCardDebugView>(); }},
|
||||
{"Temperature", ui::Color::dark_cyan(), &bitmap_icon_temperature, [&nav]() { nav.push<TemperatureView>(); }},
|
||||
{"Touch Test", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push<DebugScreenTest>(); }},
|
||||
{"Buttons Test", ui::Color::dark_cyan(), &bitmap_icon_controls, [this]() { nav_.push<DebugControlsView>(); }},
|
||||
{"Debug Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [this]() { portapack::persistent_memory::debug_dump(); }},
|
||||
{"M0 Stack Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [this]() { stack_dump(); }},
|
||||
{"Memory Dump", ui::Color::dark_cyan(), &bitmap_icon_memory, [this]() { nav_.push<DebugMemoryDumpView>(); }},
|
||||
//{"Memory Usage", ui::Color::dark_cyan(), &bitmap_icon_memory, [this]() { nav_.push<DebugMemoryView>(); }},
|
||||
{"Peripherals", ui::Color::dark_cyan(), &bitmap_icon_peripherals, [this]() { nav_.push<DebugPeripheralsMenuView>(); }},
|
||||
{"Pers. Memory", ui::Color::dark_cyan(), &bitmap_icon_memory, [this]() { nav_.push<DebugPmemView>(); }},
|
||||
//{ "Radio State", ui::Color::white(), nullptr, [this](){ nav_.push<NotImplementedView>(); } },
|
||||
{"Reboot", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<DebugReboot>(); }},
|
||||
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [this]() { nav_.push<SDCardDebugView>(); }},
|
||||
{"Temperature", ui::Color::dark_cyan(), &bitmap_icon_temperature, [this]() { nav_.push<TemperatureView>(); }},
|
||||
{"Touch Test", ui::Color::dark_cyan(), &bitmap_icon_notepad, [this]() { nav_.push<DebugScreenTest>(); }},
|
||||
});
|
||||
|
||||
for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::DEBUG, nav)) {
|
||||
for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::DEBUG, nav_)) {
|
||||
add_item(gridItem);
|
||||
};
|
||||
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
|
||||
/* DebugMemoryDumpView *********************************************************/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
* Copyright (C) 2024 u-foka
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -422,17 +423,28 @@ class DebugPeripheralsMenuView : public BtnGridView {
|
||||
public:
|
||||
DebugPeripheralsMenuView(NavigationView& nav);
|
||||
std::string title() const override { return "Peripherals"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
void on_populate() override;
|
||||
};
|
||||
|
||||
class DebugReboot : public BtnGridView {
|
||||
public:
|
||||
DebugReboot(NavigationView& nav);
|
||||
|
||||
private:
|
||||
void on_populate() override;
|
||||
};
|
||||
|
||||
class DebugMenuView : public BtnGridView {
|
||||
public:
|
||||
DebugMenuView(NavigationView& nav);
|
||||
std::string title() const override { return "Debug"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
void on_populate() override;
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -38,23 +38,29 @@ DfuMenu::DfuMenu(NavigationView& nav)
|
||||
&text_info_line_5,
|
||||
&text_info_line_6,
|
||||
&text_info_line_7,
|
||||
&text_info_line_8});
|
||||
&text_info_line_8,
|
||||
&text_info_line_9,
|
||||
&text_info_line_10});
|
||||
}
|
||||
|
||||
void DfuMenu::paint(Painter& painter) {
|
||||
auto utilisation = get_cpu_utilisation_in_percent();
|
||||
size_t m0_fragmented_free_space = 0;
|
||||
const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space);
|
||||
|
||||
text_info_line_1.set(to_string_dec_uint(chCoreStatus(), 6));
|
||||
text_info_line_2.set(to_string_dec_uint((uint32_t)get_free_stack_space(), 6));
|
||||
text_info_line_3.set(to_string_dec_uint(utilisation, 6));
|
||||
text_info_line_4.set(to_string_dec_uint(shared_memory.m4_heap_usage, 6));
|
||||
text_info_line_5.set(to_string_dec_uint(shared_memory.m4_stack_usage, 6));
|
||||
text_info_line_6.set(to_string_dec_uint(shared_memory.m4_performance_counter, 6));
|
||||
text_info_line_7.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6));
|
||||
text_info_line_8.set(to_string_dec_uint(chTimeNow() / 1000, 6));
|
||||
text_info_line_2.set(to_string_dec_uint(m0_fragmented_free_space, 6));
|
||||
text_info_line_3.set(to_string_dec_uint(m0_fragments, 6));
|
||||
text_info_line_4.set(to_string_dec_uint((uint32_t)get_free_stack_space(), 6));
|
||||
text_info_line_5.set(to_string_dec_uint(utilisation, 6));
|
||||
text_info_line_6.set(to_string_dec_uint(shared_memory.m4_heap_usage, 6));
|
||||
text_info_line_7.set(to_string_dec_uint(shared_memory.m4_stack_usage, 6));
|
||||
text_info_line_8.set(to_string_dec_uint(shared_memory.m4_performance_counter, 6));
|
||||
text_info_line_9.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6));
|
||||
text_info_line_10.set(to_string_dec_uint(chTimeNow() / 1000, 6));
|
||||
|
||||
constexpr auto margin = 5;
|
||||
constexpr auto lines = 8 + 2;
|
||||
constexpr auto lines = 10 + 2;
|
||||
|
||||
painter.fill_rectangle(
|
||||
{{6 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin},
|
||||
|
||||
@@ -49,14 +49,16 @@ class DfuMenu : public View {
|
||||
Text text_head{{6 * CHARACTER_WIDTH, 3 * LINE_HEIGHT, 11 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, "Performance"};
|
||||
|
||||
Labels labels{
|
||||
{{6 * CHARACTER_WIDTH, 5 * LINE_HEIGHT}, "M0 heap:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 6 * LINE_HEIGHT}, "M0 stack:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 7 * LINE_HEIGHT}, "M0 cpu %:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 8 * LINE_HEIGHT}, "M4 heap:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 9 * LINE_HEIGHT}, "M4 stack:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 10 * LINE_HEIGHT}, "M4 cpu %:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "M4 miss:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "uptime:", Color::dark_cyan()}};
|
||||
{{6 * CHARACTER_WIDTH, 5 * LINE_HEIGHT}, "M0 core:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 6 * LINE_HEIGHT}, "M0 heap:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 7 * LINE_HEIGHT}, "M0 frags:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 8 * LINE_HEIGHT}, "M0 stack:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 9 * LINE_HEIGHT}, "M0 cpu %:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 10 * LINE_HEIGHT}, "M4 heap:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "M4 stack:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "M4 cpu %:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "M4 miss:", Color::dark_cyan()},
|
||||
{{6 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "uptime:", Color::dark_cyan()}};
|
||||
|
||||
Text text_info_line_1{{15 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_2{{15 * CHARACTER_WIDTH, 6 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
@@ -66,6 +68,8 @@ class DfuMenu : public View {
|
||||
Text text_info_line_6{{15 * CHARACTER_WIDTH, 10 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_7{{15 * CHARACTER_WIDTH, 11 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_8{{15 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_9{{15 * CHARACTER_WIDTH, 13 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
Text text_info_line_10{{15 * CHARACTER_WIDTH, 14 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""};
|
||||
};
|
||||
|
||||
class DfuMenu2 : public View {
|
||||
|
||||
@@ -402,6 +402,7 @@ void FileSaveView::refresh_widgets() {
|
||||
void FileManagerView::refresh_widgets(const bool v) {
|
||||
button_rename.hidden(v);
|
||||
button_delete.hidden(v);
|
||||
button_clean.hidden(v);
|
||||
button_cut.hidden(v);
|
||||
button_copy.hidden(v);
|
||||
button_paste.hidden(v);
|
||||
@@ -449,7 +450,7 @@ void FileManagerView::on_rename(std::string_view hint) {
|
||||
|
||||
void FileManagerView::on_delete() {
|
||||
if (is_directory(get_selected_full_path()) && !is_empty_directory(get_selected_full_path())) {
|
||||
nav_.display_modal("Delete", "Directory not empty!");
|
||||
nav_.display_modal("Delete", " Folder is not empty;\n Use \"Clean\" button to\n empty it first.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -474,6 +475,31 @@ void FileManagerView::on_delete() {
|
||||
});
|
||||
}
|
||||
|
||||
void FileManagerView::on_clean() {
|
||||
if (is_empty_directory(get_selected_full_path())) {
|
||||
nav_.display_modal("Clean", "Folder is Empty;\nUse \"Delete\" button instead\nof \"Clean\" button to delete\nit.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto path_name = is_directory(get_selected_full_path()) ? get_selected_full_path() : get_selected_full_path().parent_path();
|
||||
|
||||
// selected either a single file (delete files in this directory) or a directory that is not empty (del sub files)
|
||||
nav_.push<ModalMessageView>(
|
||||
"Clean", " ALL FILES in this folder\n (excluding sub-folders)\n will be DELETED!\n\n Delete all files?", YESNO,
|
||||
[this, path_name](bool choice) {
|
||||
if (choice) {
|
||||
std::vector<std::filesystem::path> file_list;
|
||||
file_list = scan_root_files(path_name, u"*");
|
||||
|
||||
for (const auto& file_name : file_list) {
|
||||
std::filesystem::path current_full_path = path_name / file_name;
|
||||
delete_file(current_full_path);
|
||||
}
|
||||
reload_current();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void FileManagerView::on_new_dir() {
|
||||
name_buffer = "";
|
||||
text_prompt(nav_, name_buffer, max_filename_length, [this](std::string& dir_name) {
|
||||
@@ -560,6 +586,7 @@ FileManagerView::FileManagerView(
|
||||
&text_date,
|
||||
&button_rename,
|
||||
&button_delete,
|
||||
&button_clean,
|
||||
&button_cut,
|
||||
&button_copy,
|
||||
&button_paste,
|
||||
@@ -606,6 +633,11 @@ FileManagerView::FileManagerView(
|
||||
on_delete();
|
||||
};
|
||||
|
||||
button_clean.on_select = [this]() {
|
||||
if (selected_is_valid())
|
||||
on_clean();
|
||||
};
|
||||
|
||||
button_cut.on_select = [this]() {
|
||||
if (selected_is_valid() && !get_selected_entry().is_directory) {
|
||||
clipboard_path = get_selected_full_path();
|
||||
|
||||
@@ -207,6 +207,7 @@ class FileManagerView : public FileManBaseView {
|
||||
void refresh_widgets(const bool v);
|
||||
void on_rename(std::string_view hint);
|
||||
void on_delete();
|
||||
void on_clean();
|
||||
void on_paste();
|
||||
void on_new_dir();
|
||||
void on_new_file();
|
||||
@@ -227,11 +228,17 @@ class FileManagerView : public FileManBaseView {
|
||||
Color::dark_blue()};
|
||||
|
||||
NewButton button_delete{
|
||||
{4 * 8, 29 * 8, 4 * 8, 32},
|
||||
{9 * 8, 34 * 8, 4 * 8, 32},
|
||||
{},
|
||||
&bitmap_icon_trash,
|
||||
Color::red()};
|
||||
|
||||
NewButton button_clean{
|
||||
{13 * 8, 34 * 8, 4 * 8, 32},
|
||||
{},
|
||||
&bitmap_icon_clean,
|
||||
Color::red()};
|
||||
|
||||
NewButton button_cut{
|
||||
{9 * 8, 29 * 8, 4 * 8, 32},
|
||||
{},
|
||||
@@ -269,20 +276,22 @@ class FileManagerView : public FileManBaseView {
|
||||
Color::orange()};
|
||||
|
||||
NewButton button_rename_timestamp{
|
||||
{4 * 8, 34 * 8, 4 * 8, 32},
|
||||
|
||||
{4 * 8, 29 * 8, 4 * 8, 32},
|
||||
{},
|
||||
&bitmap_icon_options_datetime,
|
||||
Color::orange(),
|
||||
/*vcenter*/ true};
|
||||
|
||||
NewButton button_open_iq_trim{
|
||||
{9 * 8, 34 * 8, 4 * 8, 32},
|
||||
|
||||
{4 * 8, 34 * 8, 4 * 8, 32},
|
||||
{},
|
||||
&bitmap_icon_trim,
|
||||
Color::orange()};
|
||||
|
||||
NewButton button_show_hidden_files{
|
||||
{13 * 8, 34 * 8, 4 * 8, 32},
|
||||
{17 * 8, 34 * 8, 4 * 8, 32},
|
||||
{},
|
||||
&bitmap_icon_hide,
|
||||
Color::dark_grey()};
|
||||
|
||||
@@ -26,29 +26,51 @@
|
||||
|
||||
namespace ui {
|
||||
|
||||
static const char16_t* firmware_folder = u"/FIRMWARE";
|
||||
static const std::filesystem::path firmware_path = u"/FIRMWARE";
|
||||
|
||||
// Firmware image validation
|
||||
static const char* hackrf_magic = "HACKRFFW";
|
||||
#define FIRMWARE_INFO_AREA_OFFSET 0x400
|
||||
#define FIRST_CHECKSUM_NIGHTLY 240125
|
||||
|
||||
Thread* FlashUtilityView::thread{nullptr};
|
||||
static constexpr size_t max_filename_length = 26;
|
||||
|
||||
bool valid_firmware_file(std::filesystem::path::string_type path) {
|
||||
File firmware_file;
|
||||
bool require_checksum{false};
|
||||
uint32_t read_buffer[128];
|
||||
uint32_t checksum{(uint32_t)~FLASH_EXPECTED_CHECKSUM}; // initializing to invalid checksum in case file can't be read
|
||||
uint32_t checksum{FLASH_CHECKSUM_ERROR}; // initializing to invalid checksum in case file can't be read
|
||||
|
||||
static_assert((FIRMWARE_INFO_AREA_OFFSET % sizeof(read_buffer)) == 0, "Read buffer size must divide evenly into FIRMWARE_INFO_AREA_OFFSET");
|
||||
|
||||
// test read of the whole file just to validate checksum (baseband flash code will re-read when flashing)
|
||||
auto result = firmware_file.open(path.c_str());
|
||||
if (!result.is_valid()) {
|
||||
checksum = 0;
|
||||
for (uint32_t i = 0; i < FLASH_ROM_SIZE / sizeof(read_buffer); i++) {
|
||||
for (uint32_t offset = 0; offset < FLASH_ROM_SIZE; offset += sizeof(read_buffer)) {
|
||||
auto readResult = firmware_file.read(&read_buffer, sizeof(read_buffer));
|
||||
|
||||
// if file is smaller than 1MB, assume it's a downgrade to an old FW version and ignore the checksum
|
||||
if ((!readResult) || (readResult.value() != sizeof(read_buffer))) {
|
||||
checksum = FLASH_EXPECTED_CHECKSUM;
|
||||
// File was smaller than 1MB:
|
||||
// If version is such that the file SHOULD have been 1MB, call it a checksum error (otherwise say it's OK).
|
||||
checksum = (require_checksum) ? FLASH_CHECKSUM_ERROR : FLASH_EXPECTED_CHECKSUM;
|
||||
break;
|
||||
}
|
||||
|
||||
// Did we just read the firmware info area?
|
||||
if (offset == FIRMWARE_INFO_AREA_OFFSET) {
|
||||
// If there's no info area (missing HACKRFFW signature), it could be an ancient FW version (so skipping check)
|
||||
if (memcmp(read_buffer, hackrf_magic, 8) == 0) {
|
||||
char* version_string = (char*)&read_buffer[4];
|
||||
|
||||
// Require a 1MB firmware image with a valid checksum if release version >=v2.x or nightly >n_240125
|
||||
if (((version_string[0] == 'v') && (std::atoi(&version_string[1]) >= 2)) ||
|
||||
((version_string[0] == 'n') && (version_string[1] == '_') && (std::atoi(&version_string[2]) >= FIRST_CHECKSUM_NIGHTLY)))
|
||||
require_checksum = true;
|
||||
}
|
||||
}
|
||||
|
||||
checksum += simple_checksum((uint32_t)read_buffer, sizeof(read_buffer));
|
||||
}
|
||||
}
|
||||
@@ -62,30 +84,29 @@ FlashUtilityView::FlashUtilityView(NavigationView& nav)
|
||||
|
||||
menu_view.set_parent_rect({0, 3 * 8, 240, 33 * 8});
|
||||
|
||||
ensure_directory(firmware_folder);
|
||||
ensure_directory(firmware_path);
|
||||
|
||||
for (const auto& entry : std::filesystem::directory_iterator(firmware_folder, u"*.bin")) {
|
||||
auto filename = entry.path().filename();
|
||||
auto path = entry.path().native();
|
||||
auto add_firmware_items = [&](
|
||||
const std::filesystem::path& folder_path,
|
||||
const std::filesystem::path& wild,
|
||||
ui::Color color) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(folder_path, wild)) {
|
||||
auto filename = entry.path().filename();
|
||||
auto path = entry.path().native();
|
||||
|
||||
menu_view.add_item({filename.string().substr(0, max_filename_length),
|
||||
ui::Color::red(),
|
||||
&bitmap_icon_temperature,
|
||||
[this, path](KeyEvent) {
|
||||
this->firmware_selected(path);
|
||||
}});
|
||||
}
|
||||
for (const auto& entry : std::filesystem::directory_iterator(firmware_folder, u"*.tar")) {
|
||||
auto filename = entry.path().filename();
|
||||
auto path = entry.path().native();
|
||||
menu_view.add_item({filename.string().substr(0, max_filename_length),
|
||||
color,
|
||||
&bitmap_icon_temperature,
|
||||
[this, path](KeyEvent) {
|
||||
this->firmware_selected(path);
|
||||
}});
|
||||
}
|
||||
};
|
||||
|
||||
menu_view.add_item({filename.string().substr(0, max_filename_length),
|
||||
ui::Color::purple(),
|
||||
&bitmap_icon_temperature,
|
||||
[this, path](KeyEvent) {
|
||||
this->firmware_selected(path);
|
||||
}});
|
||||
}
|
||||
add_firmware_items(firmware_path, u"*.bin", ui::Color::red());
|
||||
add_firmware_items(firmware_path, u"*.tar", ui::Color::purple());
|
||||
|
||||
// add_firmware_items(user_firmware_folder,u"*.bin", ui::Color::purple());
|
||||
}
|
||||
|
||||
void FlashUtilityView::firmware_selected(std::filesystem::path::string_type path) {
|
||||
@@ -157,4 +178,4 @@ void FlashUtilityView::focus() {
|
||||
menu_view.focus();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui */
|
||||
@@ -35,6 +35,7 @@
|
||||
#define FLASH_ROM_SIZE 1048576
|
||||
#define FLASH_STARTING_ADDRESS 0x00000000
|
||||
#define FLASH_EXPECTED_CHECKSUM 0x00000000
|
||||
#define FLASH_CHECKSUM_ERROR 0xFFFFFFFF
|
||||
|
||||
namespace ui {
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ void FrequencyManagerView::on_edit_freq() {
|
||||
|
||||
void FrequencyManagerView::on_edit_desc() {
|
||||
temp_buffer_ = current_entry().description;
|
||||
text_prompt(nav_, temp_buffer_, desc_edit_max, [this](std::string& new_desc) {
|
||||
text_prompt(nav_, temp_buffer_, freqman_max_desc_size, [this](std::string& new_desc) {
|
||||
auto entry = current_entry();
|
||||
entry.description = std::move(new_desc);
|
||||
db_.replace_entry(current_index(), entry);
|
||||
|
||||
@@ -40,8 +40,6 @@ class FreqManBaseView : public View {
|
||||
|
||||
void focus() override;
|
||||
|
||||
static constexpr size_t desc_edit_max = 0x80;
|
||||
|
||||
protected:
|
||||
using options_t = OptionsField::options_t;
|
||||
|
||||
@@ -63,11 +61,11 @@ class FreqManBaseView : public View {
|
||||
|
||||
/* The top section (category) is 20px tall. */
|
||||
Labels label_category{
|
||||
{{0, 2}, "Category:", Color::light_grey()}};
|
||||
{{0, 2}, "F:", Color::light_grey()}};
|
||||
|
||||
OptionsField options_category{
|
||||
{9 * 8, 2},
|
||||
14 /* length */,
|
||||
{3 * 8, 2},
|
||||
20 /* length */,
|
||||
{}};
|
||||
|
||||
FreqManUIList freqlist_view{
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "baseband_api.hpp"
|
||||
#include "file.hpp"
|
||||
#include "oversample.hpp"
|
||||
#include "ui_font_fixed_8x16.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace tonekey;
|
||||
@@ -39,6 +40,8 @@ void LevelView::focus() {
|
||||
}
|
||||
|
||||
LevelView::~LevelView() {
|
||||
// reset performance counters request to default
|
||||
shared_memory.request_m4_performance_counter = 0;
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
@@ -58,12 +61,16 @@ LevelView::LevelView(NavigationView& nav)
|
||||
&text_ctcss,
|
||||
&freq_stats_rssi,
|
||||
&freq_stats_db,
|
||||
&freq_stats_rx,
|
||||
&audio_mode,
|
||||
&peak_mode,
|
||||
&rssi,
|
||||
&rssi_graph});
|
||||
|
||||
// activate vertical bar mode
|
||||
rssi.set_vertical_rssi(true);
|
||||
// activate counters for RxSat
|
||||
shared_memory.request_m4_performance_counter = 2;
|
||||
|
||||
change_mode(NFM_MODULATION); // Start on AM
|
||||
field_mode.set_by_value(NFM_MODULATION); // Reflect the mode into the manual selector
|
||||
@@ -139,13 +146,15 @@ LevelView::LevelView(NavigationView& nav)
|
||||
freqman_set_step_option_short(step_mode);
|
||||
freq_stats_rssi.set_style(&Styles::white);
|
||||
freq_stats_db.set_style(&Styles::white);
|
||||
freq_stats_rx.set_style(&Styles::white);
|
||||
}
|
||||
|
||||
void LevelView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
static int16_t last_max_db = -1000;
|
||||
static int16_t last_min_rssi = -1000;
|
||||
static int16_t last_avg_rssi = -1000;
|
||||
static int16_t last_max_rssi = -1000;
|
||||
static int16_t last_max_db = 0;
|
||||
static uint8_t last_min_rssi = 0;
|
||||
static uint8_t last_avg_rssi = 0;
|
||||
static uint8_t last_max_rssi = 0;
|
||||
static uint8_t last_rx_sat = 0;
|
||||
|
||||
rssi_graph.add_values(rssi.get_min(), rssi.get_avg(), rssi.get_max(), statistics.max_db);
|
||||
|
||||
@@ -159,8 +168,31 @@ void LevelView::on_statistics_update(const ChannelStatistics& statistics) {
|
||||
last_min_rssi = rssi_graph.get_graph_min();
|
||||
last_avg_rssi = rssi_graph.get_graph_avg();
|
||||
last_max_rssi = rssi_graph.get_graph_max();
|
||||
freq_stats_rssi.set("RSSI: " + to_string_dec_int(last_min_rssi) + "/" + to_string_dec_int(last_avg_rssi) + "/" + to_string_dec_int(last_max_rssi) + ",dt: " + to_string_dec_int(rssi_graph.get_graph_delta()));
|
||||
freq_stats_rssi.set("RSSI: " + to_string_dec_uint(last_min_rssi) + "/" + to_string_dec_uint(last_avg_rssi) + "/" + to_string_dec_uint(last_max_rssi) + ", dt: " + to_string_dec_uint(rssi_graph.get_graph_delta()));
|
||||
}
|
||||
// refresh sat
|
||||
uint8_t rx_sat = ((uint32_t)shared_memory.m4_performance_counter) * 100 / 127;
|
||||
if (last_rx_sat != rx_sat) {
|
||||
last_rx_sat = rx_sat;
|
||||
freq_stats_rx.set("RxSat: " + to_string_dec_uint(rx_sat) + "%");
|
||||
uint8_t br = 0;
|
||||
uint8_t bg = 0;
|
||||
uint8_t bb = 0;
|
||||
if (rx_sat <= 80) {
|
||||
bg = (255 * rx_sat) / 80;
|
||||
bb = 255 - bg;
|
||||
} else if (rx_sat > 80) {
|
||||
br = (255 * (rx_sat - 80)) / 20;
|
||||
bg = 255 - br;
|
||||
}
|
||||
Style style_freq_stats_rx{
|
||||
.font = font::fixed_8x16,
|
||||
.background = {br, bg, bb},
|
||||
.foreground = {255, 255, 255},
|
||||
};
|
||||
freq_stats_rx.set_style(&style_freq_stats_rx);
|
||||
}
|
||||
|
||||
} /* on_statistic_updates */
|
||||
|
||||
size_t LevelView::change_mode(freqman_index_t new_mod) {
|
||||
|
||||
@@ -62,8 +62,6 @@ class LevelView : public View {
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
void set_display_freq(int64_t freq);
|
||||
|
||||
// TODO: needed?
|
||||
int32_t db{0};
|
||||
rf::Frequency freq_ = {0};
|
||||
|
||||
Labels labels{
|
||||
@@ -118,12 +116,12 @@ class LevelView : public View {
|
||||
|
||||
// RSSI: XX/XX/XXX,dt: XX
|
||||
Text freq_stats_rssi{
|
||||
{0 * 8, 3 * 16 + 4, 22 * 8, 14},
|
||||
{0 * 8, 3 * 16 + 4, 22 * 8, 1 * 16},
|
||||
};
|
||||
|
||||
// Power: -XXX db
|
||||
Text freq_stats_db{
|
||||
{0 * 8, 4 * 16 + 4, 14 * 8, 14},
|
||||
{0 * 8, 4 * 16 + 4, 15 * 8, 1 * 16},
|
||||
};
|
||||
|
||||
OptionsField peak_mode{
|
||||
@@ -138,6 +136,7 @@ class LevelView : public View {
|
||||
{"peak:5s", 5000},
|
||||
{"peak:10s", 10000},
|
||||
}};
|
||||
|
||||
OptionsField rssi_resolution{
|
||||
{44 + 20 * 8, 4 * 16 + 4},
|
||||
4,
|
||||
@@ -149,14 +148,19 @@ class LevelView : public View {
|
||||
{"240x", 240},
|
||||
}};
|
||||
|
||||
// RxSat: XX%
|
||||
Text freq_stats_rx{
|
||||
{0 * 8, 5 * 16 + 4, 10 * 8, 1 * 16},
|
||||
};
|
||||
|
||||
RSSIGraph rssi_graph{
|
||||
// 240x320 =>
|
||||
{0, 5 * 16 + 4, 240 - 5 * 8, 320 - (5 * 16 + 4)},
|
||||
{0, 6 * 16 + 4, 240 - 5 * 8, 320 - (6 * 16 + 4)},
|
||||
};
|
||||
|
||||
RSSI rssi{
|
||||
// 240x320 =>
|
||||
{240 - 5 * 8, 5 * 16 + 4, 5 * 8, 320 - (5 * 16 + 4)},
|
||||
{240 - 5 * 8, 6 * 16 + 4, 5 * 8, 320 - (6 * 16 + 4)},
|
||||
};
|
||||
|
||||
void handle_coded_squelch(const uint32_t value);
|
||||
|
||||
@@ -338,6 +338,7 @@ GlassView::GlassView(
|
||||
&field_trigger,
|
||||
&button_jump,
|
||||
&button_rst,
|
||||
&field_rx_iq_phase_cal,
|
||||
&freq_stats});
|
||||
|
||||
load_presets(); // Load available presets from TXT files (or default).
|
||||
@@ -474,6 +475,13 @@ GlassView::GlassView(
|
||||
reset_live_view();
|
||||
};
|
||||
|
||||
field_rx_iq_phase_cal.set_range(0, hackrf_r9 ? 63 : 31); // max2839 has 6 bits [0..63], max2837 has 5 bits [0..31]
|
||||
field_rx_iq_phase_cal.set_value(get_spec_iq_phase_calibration_value()); // using accessor function of AnalogAudioView to read iq_phase_calibration_value from rx_audio.ini
|
||||
field_rx_iq_phase_cal.on_change = [this](int32_t v) {
|
||||
set_spec_iq_phase_calibration_value(v); // using accessor function of AnalogAudioView to write inside SPEC submenu, register value to max283x and save it to rx_audio.ini
|
||||
};
|
||||
set_spec_iq_phase_calibration_value(get_spec_iq_phase_calibration_value()); // initialize iq_phase_calibration in radio
|
||||
|
||||
display.scroll_set_area(109, 319);
|
||||
|
||||
// trigger:
|
||||
@@ -491,6 +499,15 @@ GlassView::GlassView(
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
uint8_t GlassView::get_spec_iq_phase_calibration_value() { // define accessor functions inside AnalogAudioView to read & write real iq_phase_calibration_value
|
||||
return iq_phase_calibration_value;
|
||||
}
|
||||
|
||||
void GlassView::set_spec_iq_phase_calibration_value(uint8_t cal_value) { // define accessor functions
|
||||
iq_phase_calibration_value = cal_value;
|
||||
radio::set_rx_max283x_iq_phase_calibration(iq_phase_calibration_value);
|
||||
}
|
||||
|
||||
void GlassView::load_presets() {
|
||||
File presets_file;
|
||||
auto error = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
|
||||
|
||||
@@ -69,6 +69,9 @@ class GlassView : public View {
|
||||
void on_hide() override;
|
||||
void focus() override;
|
||||
|
||||
uint8_t get_spec_iq_phase_calibration_value();
|
||||
void set_spec_iq_phase_calibration_value(uint8_t cal_value);
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
RxRadioState radio_state_{ReceiverModel::Mode::SpectrumAnalysis};
|
||||
@@ -79,8 +82,9 @@ class GlassView : public View {
|
||||
uint8_t filter_index = 0; // OFF
|
||||
uint8_t trigger = 32;
|
||||
uint8_t mode = LOOKING_GLASS_FASTSCAN;
|
||||
uint8_t live_frequency_view = 0; // Spectrum
|
||||
uint8_t live_frequency_integrate = 3; // Default (3 * old value + new_value) / 4
|
||||
uint8_t live_frequency_view = 0; // Spectrum
|
||||
uint8_t live_frequency_integrate = 3; // Default (3 * old value + new_value) / 4
|
||||
uint8_t iq_phase_calibration_value{15}; // initial default RX IQ phase calibration value , used for both max2837 & max2839
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_glass"sv,
|
||||
app_settings::Mode::RX,
|
||||
@@ -93,6 +97,7 @@ class GlassView : public View {
|
||||
{"scan_mode"sv, &mode},
|
||||
{"freq_view"sv, &live_frequency_view},
|
||||
{"freq_integrate"sv, &live_frequency_integrate},
|
||||
{"iq_phase_calibration"sv, &iq_phase_calibration_value}, // we are saving and restoring that CAL from Settings.
|
||||
}};
|
||||
|
||||
struct preset_entry {
|
||||
@@ -160,7 +165,7 @@ class GlassView : public View {
|
||||
{{0, 0 * 16}, "MIN: MAX: LNA VGA ", Color::light_grey()},
|
||||
{{0, 1 * 16}, "RANGE: FILTER: AMP:", Color::light_grey()},
|
||||
{{0, 2 * 16}, "PRESET:", Color::light_grey()},
|
||||
{{0, 3 * 16}, "MARKER: MHz", Color::light_grey()},
|
||||
{{0, 3 * 16}, "MARKER: MHz RXIQCAL", Color::light_grey()},
|
||||
{{0, 4 * 16}, "RES: STEP:", Color::light_grey()}};
|
||||
|
||||
NumberField field_frequency_min{
|
||||
@@ -208,6 +213,14 @@ class GlassView : public View {
|
||||
{7 * 8, 3 * 16, 9 * 8, 16},
|
||||
""};
|
||||
|
||||
NumberField field_rx_iq_phase_cal{
|
||||
{28 * 8, 3 * 16},
|
||||
2,
|
||||
{0, 63}, // 5 or 6 bits IQ CAL phase adjustment (range updated later)
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
NumberField field_trigger{
|
||||
{4 * 8, 4 * 16},
|
||||
3,
|
||||
|
||||
@@ -338,7 +338,7 @@ MicTXView::MicTXView(
|
||||
&field_rxlna,
|
||||
&field_rxvga,
|
||||
&field_rxamp,
|
||||
hackrf_r9 ? &field_tx_iq_phase_cal_2839 : &field_tx_iq_phase_cal_2837,
|
||||
&field_tx_iq_phase_cal,
|
||||
&tx_button,
|
||||
&tx_icon});
|
||||
|
||||
@@ -372,19 +372,12 @@ MicTXView::MicTXView(
|
||||
};
|
||||
|
||||
radio::set_tx_max283x_iq_phase_calibration(iq_phase_calibration_value);
|
||||
if (hackrf_r9) { // MAX2839 has 6 bits IQ CAL phasse adjustment.
|
||||
field_tx_iq_phase_cal_2839.set_value(iq_phase_calibration_value);
|
||||
field_tx_iq_phase_cal_2839.on_change = [this](int32_t v) {
|
||||
iq_phase_calibration_value = v;
|
||||
radio::set_tx_max283x_iq_phase_calibration(iq_phase_calibration_value);
|
||||
};
|
||||
} else { // MAX2837 has 5 bits IQ CAL phase adjustment.
|
||||
field_tx_iq_phase_cal_2837.set_value(iq_phase_calibration_value);
|
||||
field_tx_iq_phase_cal_2837.on_change = [this](int32_t v) {
|
||||
iq_phase_calibration_value = v;
|
||||
radio::set_tx_max283x_iq_phase_calibration(iq_phase_calibration_value);
|
||||
};
|
||||
}
|
||||
field_tx_iq_phase_cal.set_range(0, hackrf_r9 ? 63 : 31); // max2839 has 6 bits [0..63], max2837 has 5 bits [0..31]
|
||||
field_tx_iq_phase_cal.set_value(iq_phase_calibration_value);
|
||||
field_tx_iq_phase_cal.on_change = [this](int32_t v) {
|
||||
iq_phase_calibration_value = v;
|
||||
radio::set_tx_max283x_iq_phase_calibration(iq_phase_calibration_value);
|
||||
};
|
||||
|
||||
options_gain.on_change = [this](size_t, int32_t v) {
|
||||
mic_gain_x10 = v;
|
||||
|
||||
@@ -342,18 +342,10 @@ class MicTXView : public View {
|
||||
' ',
|
||||
};
|
||||
|
||||
NumberField field_tx_iq_phase_cal_2837{
|
||||
NumberField field_tx_iq_phase_cal{
|
||||
{24 * 8, (33 * 8)},
|
||||
2,
|
||||
{0, 31}, // 5 bits IQ CAL phase adjustment.
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
NumberField field_tx_iq_phase_cal_2839{
|
||||
{24 * 8, (33 * 8)},
|
||||
2,
|
||||
{0, 63}, // 6 bits IQ CAL phasse adjustment.
|
||||
{0, 63}, // 5 or 6 bits IQ CAL phase adjustment (range updated later)
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
@@ -49,7 +49,13 @@ namespace fs = std::filesystem;
|
||||
namespace ui {
|
||||
|
||||
void ReconView::reload_restart_recon() {
|
||||
// force reload of current
|
||||
change_mode(field_mode.selected_index_value());
|
||||
uint8_t previous_index = current_index;
|
||||
reset_indexes();
|
||||
frequency_file_load();
|
||||
current_index = previous_index;
|
||||
handle_retune();
|
||||
if (frequency_list.size() > 0) {
|
||||
if (fwd) {
|
||||
button_dir.set_text("FW>");
|
||||
@@ -104,18 +110,19 @@ void ReconView::set_loop_config(bool v) {
|
||||
|
||||
void ReconView::recon_stop_recording(bool exiting) {
|
||||
if (is_recording) {
|
||||
if (field_mode.selected_index_value() == SPEC_MODULATION)
|
||||
button_audio_app.set_text("RAW");
|
||||
else
|
||||
button_audio_app.set_text("AUDIO");
|
||||
button_audio_app.set_style(&Styles::white);
|
||||
record_view->stop();
|
||||
button_config.set_style(&Styles::white);
|
||||
is_recording = false;
|
||||
// repeater mode
|
||||
if (!exiting && persistent_memory::recon_repeat_recorded()) {
|
||||
start_repeat();
|
||||
if (field_mode.selected_index_value() == SPEC_MODULATION) {
|
||||
button_audio_app.set_text("RAW");
|
||||
// repeater mode
|
||||
if (!exiting && persistent_memory::recon_repeat_recorded()) {
|
||||
start_repeat();
|
||||
}
|
||||
} else {
|
||||
button_audio_app.set_text("AUDIO");
|
||||
}
|
||||
button_audio_app.set_style(&Styles::white);
|
||||
button_config.set_style(&Styles::white);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1148,7 +1155,7 @@ void ReconView::on_stepper_delta(int32_t v) {
|
||||
}
|
||||
|
||||
size_t ReconView::change_mode(freqman_index_t new_mod) {
|
||||
if (recon_tx || is_repeat_active())
|
||||
if (recon_tx || is_repeat_active() || is_recording)
|
||||
return 0;
|
||||
field_mode.on_change = [this](size_t, OptionsField::value_t) {};
|
||||
field_bw.on_change = [this](size_t, OptionsField::value_t) {};
|
||||
@@ -1157,16 +1164,21 @@ size_t ReconView::change_mode(freqman_index_t new_mod) {
|
||||
remove_child(record_view.get());
|
||||
record_view.reset();
|
||||
}
|
||||
if (persistent_memory::recon_repeat_recorded()) {
|
||||
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
|
||||
u"RECON_REPEAT.C16", u"CAPTURES",
|
||||
RecordView::FileType::RawS16, 16384, 3);
|
||||
record_view->set_filename_as_is(true);
|
||||
} else if (new_mod == SPEC_MODULATION) {
|
||||
if (field_mode.selected_index_value() != SPEC_MODULATION) {
|
||||
audio::output::stop();
|
||||
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
|
||||
u"AUTO_RAW", u"CAPTURES",
|
||||
RecordView::FileType::RawS16, 16384, 3);
|
||||
}
|
||||
if (new_mod == SPEC_MODULATION) {
|
||||
if (persistent_memory::recon_repeat_recorded()) {
|
||||
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
|
||||
u"RECON_REPEAT.C16", u"CAPTURES",
|
||||
RecordView::FileType::RawS16, 16384, 3);
|
||||
record_view->set_filename_as_is(true);
|
||||
} else {
|
||||
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
|
||||
u"AUTO_RAW", u"CAPTURES",
|
||||
RecordView::FileType::RawS16, 16384, 3);
|
||||
record_view->set_filename_date_frequency(true);
|
||||
}
|
||||
} else {
|
||||
record_view = std::make_unique<RecordView>(Rect{0, 0, 30 * 8, 1 * 16},
|
||||
u"AUTO_AUDIO", u"AUDIO",
|
||||
@@ -1314,9 +1326,6 @@ bool ReconView::is_repeat_active() const {
|
||||
|
||||
void ReconView::start_repeat() {
|
||||
// Prepare to send a file.
|
||||
std::filesystem::path rawfile = u"/" + repeat_rec_path + u"/" + repeat_rec_file;
|
||||
std::filesystem::path rawmeta = u"/" + repeat_rec_path + u"/" + repeat_rec_meta;
|
||||
|
||||
if (recon_tx == false) {
|
||||
recon_tx = true;
|
||||
|
||||
@@ -1434,6 +1443,12 @@ void ReconView::stop_repeat(const bool do_loop) {
|
||||
} else {
|
||||
repeat_cur_rep = 0;
|
||||
recon_tx = false;
|
||||
if (persistent_memory::recon_repeat_recorded_file_mode() == RECON_REPEAT_AND_KEEP) {
|
||||
// rename file here to keep
|
||||
std::filesystem::path base_path = next_filename_matching_pattern(repeat_rec_path / u"REC_????.*");
|
||||
rename_file(rawfile, base_path.replace_extension(u".C16"));
|
||||
rename_file(rawmeta, base_path.replace_extension(u".TXT"));
|
||||
}
|
||||
reload_restart_recon();
|
||||
progressbar.hidden(true);
|
||||
set_dirty(); // fix progressbar no hiding
|
||||
|
||||
@@ -198,6 +198,9 @@ class ReconView : public View {
|
||||
bool repeat_ready_signal{false};
|
||||
bool recon_tx{false};
|
||||
|
||||
std::filesystem::path rawfile = u"/" + repeat_rec_path + u"/" + repeat_rec_file;
|
||||
std::filesystem::path rawmeta = u"/" + repeat_rec_path + u"/" + repeat_rec_meta;
|
||||
|
||||
// Persisted settings.
|
||||
SettingsStore ui_settings{
|
||||
"recon"sv,
|
||||
|
||||
@@ -103,6 +103,7 @@ void ReconSetupViewMore::save() {
|
||||
persistent_memory::set_recon_update_ranges_when_recon(checkbox_update_ranges_when_recon.value());
|
||||
persistent_memory::set_recon_auto_record_locked(checkbox_auto_record_locked.value());
|
||||
persistent_memory::set_recon_repeat_recorded(checkbox_repeat_recorded.value());
|
||||
persistent_memory::set_recon_repeat_recorded_file_mode(field_repeat_file_mode.selected_index_value());
|
||||
persistent_memory::set_recon_repeat_nb(field_repeat_nb.value());
|
||||
persistent_memory::set_recon_repeat_amp(checkbox_repeat_amp.value());
|
||||
persistent_memory::set_recon_repeat_gain(field_repeat_gain.value());
|
||||
@@ -125,6 +126,7 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
|
||||
&checkbox_update_ranges_when_recon,
|
||||
&checkbox_auto_record_locked,
|
||||
&checkbox_repeat_recorded,
|
||||
&field_repeat_file_mode,
|
||||
&text_repeat_nb,
|
||||
&field_repeat_nb,
|
||||
&checkbox_repeat_amp,
|
||||
@@ -135,6 +137,7 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
|
||||
|
||||
// tx options have to be in yellow to inform the users that activating them will make the device transmit
|
||||
checkbox_repeat_recorded.set_style(&Styles::yellow);
|
||||
field_repeat_file_mode.set_style(&Styles::yellow);
|
||||
text_repeat_nb.set_style(&Styles::yellow);
|
||||
field_repeat_nb.set_style(&Styles::yellow);
|
||||
checkbox_repeat_amp.set_style(&Styles::yellow);
|
||||
@@ -150,6 +153,7 @@ ReconSetupViewMore::ReconSetupViewMore(NavigationView& nav, Rect parent_rect)
|
||||
checkbox_update_ranges_when_recon.set_value(persistent_memory::recon_update_ranges_when_recon());
|
||||
checkbox_auto_record_locked.set_value(persistent_memory::recon_auto_record_locked());
|
||||
checkbox_repeat_recorded.set_value(persistent_memory::recon_repeat_recorded());
|
||||
field_repeat_file_mode.set_selected_index(persistent_memory::recon_repeat_recorded_file_mode());
|
||||
checkbox_repeat_amp.set_value(persistent_memory::recon_repeat_amp());
|
||||
field_repeat_nb.set_value(persistent_memory::recon_repeat_nb());
|
||||
field_repeat_gain.set_value(persistent_memory::recon_repeat_gain());
|
||||
|
||||
@@ -38,16 +38,19 @@
|
||||
#endif
|
||||
#define OneMHz 1000000
|
||||
|
||||
// modes
|
||||
// main app mode
|
||||
#define RECON_MATCH_CONTINUOUS 0
|
||||
#define RECON_MATCH_SPARSE 1
|
||||
|
||||
// repeater mode
|
||||
#define RECON_REPEAT_AND_DELETE 0
|
||||
#define RECON_REPEAT_AND_KEEP 1
|
||||
|
||||
// statistics update interval in ms (change here if the statistics API is changing it's pace)
|
||||
#define STATS_UPDATE_INTERVAL 100
|
||||
|
||||
// maximum lock duration
|
||||
#define RECON_MAX_LOCK_DURATION 9900
|
||||
|
||||
#define RECON_DEF_SQUELCH -14
|
||||
|
||||
// default number of match to have a lock
|
||||
@@ -150,15 +153,21 @@ class ReconSetupViewMore : public View {
|
||||
|
||||
Checkbox checkbox_repeat_recorded{
|
||||
{1 * 8, 162},
|
||||
3,
|
||||
"repeater,"};
|
||||
0,
|
||||
""};
|
||||
|
||||
OptionsField field_repeat_file_mode{
|
||||
{4 * 8 + 3, 165},
|
||||
13,
|
||||
{{"repeat,delete", RECON_REPEAT_AND_DELETE},
|
||||
{"repeat,keep ", RECON_REPEAT_AND_KEEP}}};
|
||||
|
||||
Text text_repeat_nb{
|
||||
{14 * 8, 165, 3 * 8, 22},
|
||||
{20 * 8, 165, 3 * 8, 22},
|
||||
"nb:"};
|
||||
|
||||
NumberField field_repeat_nb{
|
||||
{17 * 8, 165},
|
||||
{23 * 8, 165},
|
||||
2,
|
||||
{1, 99},
|
||||
1,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
|
||||
* Copyright (C) 2023 Kyle Reed
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
* Copyright (C) 2024 u-foka
|
||||
* Copyleft (ɔ) 2024 zxkmm under GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
@@ -835,28 +836,32 @@ void SetMenuColorView::focus() {
|
||||
|
||||
/* SettingsMenuView **************************************/
|
||||
|
||||
SettingsMenuView::SettingsMenuView(NavigationView& nav) {
|
||||
if (pmem::show_gui_return_icon()) {
|
||||
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
|
||||
}
|
||||
add_items({
|
||||
{"App Settings", ui::Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push<AppSettingsView>(); }},
|
||||
{"Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [&nav]() { nav.push<SetAudioView>(); }},
|
||||
{"Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [&nav]() { nav.push<TouchCalibrationView>(); }},
|
||||
{"Config Mode", ui::Color::dark_cyan(), &bitmap_icon_clk_ext, [&nav]() { nav.push<SetConfigModeView>(); }},
|
||||
{"Converter", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push<SetConverterSettingsView>(); }},
|
||||
{"Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [&nav]() { nav.push<SetDateTimeView>(); }},
|
||||
{"Encoder Dial", ui::Color::dark_cyan(), &bitmap_icon_setup, [&nav]() { nav.push<SetEncoderDialView>(); }},
|
||||
{"Freq. Correct", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push<SetFrequencyCorrectionView>(); }},
|
||||
{"P.Memory Mgmt", ui::Color::dark_cyan(), &bitmap_icon_memory, [&nav]() { nav.push<SetPersistentMemoryView>(); }},
|
||||
{"Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [&nav]() { nav.push<SetRadioView>(); }},
|
||||
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [&nav]() { nav.push<SetSDCardView>(); }},
|
||||
{"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [&nav]() { nav.push<SetUIView>(); }},
|
||||
{"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [&nav]() { nav.push<SetQRCodeView>(); }},
|
||||
{"Brightness", ui::Color::dark_cyan(), &bitmap_icon_brightness, [&nav]() { nav.push<SetFakeBrightnessView>(); }},
|
||||
{"Menu Color", ui::Color::dark_cyan(), &bitmap_icon_brightness, [&nav]() { nav.push<SetMenuColorView>(); }},
|
||||
});
|
||||
SettingsMenuView::SettingsMenuView(NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
|
||||
void SettingsMenuView::on_populate() {
|
||||
if (pmem::show_gui_return_icon()) {
|
||||
add_items({{"..", ui::Color::light_grey(), &bitmap_icon_previous, [this]() { nav_.pop(); }}});
|
||||
}
|
||||
add_items({
|
||||
{"App Settings", ui::Color::dark_cyan(), &bitmap_icon_notepad, [this]() { nav_.push<AppSettingsView>(); }},
|
||||
{"Audio", ui::Color::dark_cyan(), &bitmap_icon_speaker, [this]() { nav_.push<SetAudioView>(); }},
|
||||
{"Calibration", ui::Color::dark_cyan(), &bitmap_icon_options_touch, [this]() { nav_.push<TouchCalibrationView>(); }},
|
||||
{"Config Mode", ui::Color::dark_cyan(), &bitmap_icon_clk_ext, [this]() { nav_.push<SetConfigModeView>(); }},
|
||||
{"Converter", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push<SetConverterSettingsView>(); }},
|
||||
{"Date/Time", ui::Color::dark_cyan(), &bitmap_icon_options_datetime, [this]() { nav_.push<SetDateTimeView>(); }},
|
||||
{"Encoder Dial", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetEncoderDialView>(); }},
|
||||
{"Freq. Correct", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push<SetFrequencyCorrectionView>(); }},
|
||||
{"P.Memory Mgmt", ui::Color::dark_cyan(), &bitmap_icon_memory, [this]() { nav_.push<SetPersistentMemoryView>(); }},
|
||||
{"Radio", ui::Color::dark_cyan(), &bitmap_icon_options_radio, [this]() { nav_.push<SetRadioView>(); }},
|
||||
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [this]() { nav_.push<SetSDCardView>(); }},
|
||||
{"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [this]() { nav_.push<SetUIView>(); }},
|
||||
{"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [this]() { nav_.push<SetQRCodeView>(); }},
|
||||
{"Brightness", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetFakeBrightnessView>(); }},
|
||||
{"Menu Color", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetMenuColorView>(); }},
|
||||
});
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
* Copyright (C) 2023 gullradriel, Nilorea Studio Inc.
|
||||
* Copyright (C) 2023 Kyle Reed
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
* Copyright (C) 2024 u-foka
|
||||
* Copyleft (ɔ) 2024 zxkmm under GPL license
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
@@ -792,8 +793,11 @@ class SetMenuColorView : public View {
|
||||
class SettingsMenuView : public BtnGridView {
|
||||
public:
|
||||
SettingsMenuView(NavigationView& nav);
|
||||
|
||||
std::string title() const override { return "Settings"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
void on_populate() override;
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
#include "log_file.hpp"
|
||||
#include "string_format.hpp"
|
||||
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
@@ -556,8 +554,6 @@ void TextEditorView::open_file(const fs::path& path) {
|
||||
viewer.set_file(*file_);
|
||||
}
|
||||
|
||||
portapack::persistent_memory::set_apply_fake_brightness(false); // work around to resolve the display issue in notepad app. not elegant i know, so TODO.
|
||||
|
||||
refresh_ui();
|
||||
}
|
||||
|
||||
|
||||
@@ -5764,8 +5764,8 @@ static constexpr uint8_t bitmap_icon_brightness_data[] = {
|
||||
0x00,
|
||||
0x80,
|
||||
0x01,
|
||||
0x80,
|
||||
0x01,
|
||||
0x84,
|
||||
0x21,
|
||||
0x08,
|
||||
0x10,
|
||||
0xC0,
|
||||
@@ -5786,8 +5786,8 @@ static constexpr uint8_t bitmap_icon_brightness_data[] = {
|
||||
0x03,
|
||||
0x08,
|
||||
0x10,
|
||||
0x80,
|
||||
0x01,
|
||||
0x84,
|
||||
0x21,
|
||||
0x80,
|
||||
0x01,
|
||||
0x00,
|
||||
@@ -5797,6 +5797,44 @@ static constexpr Bitmap bitmap_icon_brightness{
|
||||
{16, 16},
|
||||
bitmap_icon_brightness_data};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_clean_data[] = {
|
||||
0x00,
|
||||
0x00,
|
||||
0xC0,
|
||||
0x01,
|
||||
0x20,
|
||||
0x02,
|
||||
0xFC,
|
||||
0x1F,
|
||||
0x00,
|
||||
0x00,
|
||||
0x08,
|
||||
0x08,
|
||||
0xE8,
|
||||
0x08,
|
||||
0xA8,
|
||||
0x09,
|
||||
0xA8,
|
||||
0x0B,
|
||||
0x28,
|
||||
0x0A,
|
||||
0x28,
|
||||
0x0A,
|
||||
0x28,
|
||||
0x0A,
|
||||
0xE8,
|
||||
0x0B,
|
||||
0x08,
|
||||
0x08,
|
||||
0xF0,
|
||||
0x07,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
static constexpr Bitmap bitmap_icon_clean{
|
||||
{16, 16},
|
||||
bitmap_icon_clean_data};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
#endif /*__BITMAP_HPP__*/
|
||||
|
||||
+79
-20
@@ -20,7 +20,7 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
// Code from https://github.com/Flipper-XFW/Xtreme-Apps/tree/04c3a60093e2c2378e79498b4505aa8072980a42/ble_spam/protocols
|
||||
// Code from https://github.com/Next-Flip/Momentum-Apps/blob/dev/ble_spam/
|
||||
// Thanks for the work of the original creators!
|
||||
|
||||
#include "ui_blespam.hpp"
|
||||
@@ -109,7 +109,8 @@ BLESpamView::BLESpamView(NavigationView& nav)
|
||||
console.writeln("Based on work of:");
|
||||
console.writeln("@Willy-JL, @ECTO-1A,");
|
||||
console.writeln("@Spooks4576, @iNetro");
|
||||
console.writeln("");
|
||||
console.writeln("---");
|
||||
console.writeln("iOS crash + Android\nattacks are patched\non new devices.");
|
||||
#endif
|
||||
changePacket(true); // init
|
||||
}
|
||||
@@ -293,6 +294,39 @@ void BLESpamView::createWindowsPacket() {
|
||||
std::copy(res.begin(), res.end(), advertisementData);
|
||||
}
|
||||
|
||||
void BLESpamView::createNameSpamPacket() {
|
||||
const char* names[] = {"PortaHack", "PwnBt", "iSpam", "GenericFoodVagon", "SignalSnoop", "ByteBandit", "RadioRogue", "RadioRebel", "ByteBlast"};
|
||||
|
||||
const char* name = names[rand() % 9]; //"PortaHack";
|
||||
uint8_t name_len = strlen(name);
|
||||
|
||||
uint8_t size = 12 + name_len;
|
||||
uint8_t i = 0;
|
||||
|
||||
packet[i++] = 2; // Size
|
||||
packet[i++] = 0x01; // AD Type (Flags)
|
||||
packet[i++] = 0x06; // Flags
|
||||
|
||||
packet[i++] = name_len + 1; // Size
|
||||
packet[i++] = 0x09; // AD Type (Complete Local Name)
|
||||
memcpy(&packet[i], name, name_len); // Device Name
|
||||
i += name_len;
|
||||
|
||||
packet[i++] = 3; // Size
|
||||
packet[i++] = 0x02; // AD Type (Incomplete Service UUID List)
|
||||
packet[i++] = 0x12; // Service UUID (Human Interface Device)
|
||||
packet[i++] = 0x18; // ...
|
||||
|
||||
packet[i++] = 2; // Size
|
||||
packet[i++] = 0x0A; // AD Type (Tx Power Level)
|
||||
packet[i++] = 0x00; // 0dBm
|
||||
|
||||
// size, packet
|
||||
std::string res = to_string_hex_array(packet, size);
|
||||
memset(advertisementData, 0, sizeof(advertisementData));
|
||||
std::copy(res.begin(), res.end(), advertisementData);
|
||||
}
|
||||
|
||||
void BLESpamView::createIosPacket(bool crash = false) {
|
||||
uint8_t ios_packet_sizes[18] = {0, 0, 0, 0, 0, 24, 0, 31, 0, 12, 0, 0, 20, 0, 12, 11, 11, 17};
|
||||
ContinuityType type;
|
||||
@@ -509,6 +543,48 @@ void BLESpamView::createFastPairPacket() {
|
||||
std::copy(res.begin(), res.end(), advertisementData);
|
||||
}
|
||||
|
||||
void BLESpamView::createAnyPacket(bool safe) {
|
||||
ATK_TYPE type[] = {
|
||||
ATK_ANDROID,
|
||||
ATK_IOS,
|
||||
ATK_WINDOWS,
|
||||
ATK_SAMSUNG,
|
||||
ATK_NAMESPAM,
|
||||
ATK_IOS_CRASH};
|
||||
ATK_TYPE attackType = type[rand() % (COUNT_OF(type) - (1 ? safe : 0))];
|
||||
createPacket(attackType);
|
||||
}
|
||||
|
||||
void BLESpamView::createPacket(ATK_TYPE attackType) {
|
||||
switch (attackType) {
|
||||
case ATK_IOS_CRASH:
|
||||
createIosPacket(true);
|
||||
break;
|
||||
case ATK_IOS:
|
||||
createIosPacket(false);
|
||||
break;
|
||||
case ATK_SAMSUNG:
|
||||
createSamsungPacket();
|
||||
break;
|
||||
case ATK_WINDOWS:
|
||||
createWindowsPacket();
|
||||
break;
|
||||
case ATK_NAMESPAM:
|
||||
createNameSpamPacket();
|
||||
break;
|
||||
case ATK_ALL_SAFE:
|
||||
createAnyPacket(true);
|
||||
break;
|
||||
case ATK_ALL:
|
||||
createAnyPacket(false);
|
||||
break;
|
||||
default:
|
||||
case ATK_ANDROID:
|
||||
createFastPairPacket();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BLESpamView::changePacket(bool forced = false) {
|
||||
counter++; // need to send it multiple times to be accepted
|
||||
if (counter >= 4 || forced) {
|
||||
@@ -517,24 +593,7 @@ void BLESpamView::changePacket(bool forced = false) {
|
||||
randomizeMac();
|
||||
randomChn();
|
||||
if (randomDev || forced) {
|
||||
switch (attackType) {
|
||||
case ATK_IOS_CRASH:
|
||||
createIosPacket(true);
|
||||
break;
|
||||
case ATK_IOS:
|
||||
createIosPacket(false);
|
||||
break;
|
||||
case ATK_SAMSUNG:
|
||||
createSamsungPacket();
|
||||
break;
|
||||
case ATK_WINDOWS:
|
||||
createWindowsPacket();
|
||||
break;
|
||||
default:
|
||||
case ATK_ANDROID:
|
||||
createFastPairPacket();
|
||||
break;
|
||||
}
|
||||
createPacket(attackType);
|
||||
}
|
||||
// rate limit console display
|
||||
#ifdef BLESPMUSECONSOLE
|
||||
|
||||
+11
-2
@@ -49,7 +49,10 @@ enum ATK_TYPE {
|
||||
ATK_IOS,
|
||||
ATK_IOS_CRASH,
|
||||
ATK_WINDOWS,
|
||||
ATK_SAMSUNG
|
||||
ATK_SAMSUNG,
|
||||
ATK_NAMESPAM,
|
||||
ATK_ALL_SAFE,
|
||||
ATK_ALL
|
||||
};
|
||||
enum PKT_TYPE {
|
||||
PKT_TYPE_INVALID_TYPE,
|
||||
@@ -124,7 +127,10 @@ class BLESpamView : public View {
|
||||
{"iOs", 1},
|
||||
{"iOs crash", 2},
|
||||
{"Windows", 3},
|
||||
{"Samsung", 4}}};
|
||||
{"Samsung", 4},
|
||||
{"NameSpam", 5},
|
||||
{"All-Safe", 6},
|
||||
{"All", 7}}};
|
||||
|
||||
bool is_running{false};
|
||||
|
||||
@@ -148,6 +154,9 @@ class BLESpamView : public View {
|
||||
void createIosPacket(bool crash);
|
||||
void createSamsungPacket();
|
||||
void createWindowsPacket();
|
||||
void createNameSpamPacket();
|
||||
void createAnyPacket(bool safe);
|
||||
void createPacket(ATK_TYPE attackType);
|
||||
void changePacket(bool forced);
|
||||
void on_tx_progress(const bool done);
|
||||
|
||||
|
||||
+1
-1
@@ -1867,7 +1867,7 @@ static byte menuselect(byte lines) { // Selection (1 line = 16 items)
|
||||
// *** S T A C K
|
||||
|
||||
static void floatstack() {
|
||||
memcpy(ds, &ds[1], (DATASTACKSIZE - 1) * sizeof(double));
|
||||
memmove(ds, &ds[1], (DATASTACKSIZE - 1) * sizeof(double));
|
||||
dp--;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ __attribute__((section(".external_app.app_calculator.application_information"),
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_noop */ {'\0', '\0', '\0', '\0'}, // optional
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,6 +62,11 @@ set(EXTCPPSRC
|
||||
external/keyfob/main.cpp
|
||||
external/keyfob/ui_keyfob.cpp
|
||||
external/keyfob/ui_keyfob.hpp
|
||||
|
||||
#extsensors
|
||||
external/extsensors/main.cpp
|
||||
external/extsensors/ui_extsensors.cpp
|
||||
external/extsensors/ui_extsensors.hpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
@@ -80,4 +85,5 @@ set(EXTAPPLIST
|
||||
spainter
|
||||
keyfob
|
||||
tetris
|
||||
extsensors
|
||||
)
|
||||
|
||||
+9
@@ -38,6 +38,7 @@ MEMORY
|
||||
ram_external_app_spainter(rwx) : org = 0xADBC0000, len = 32k
|
||||
ram_external_app_keyfob(rwx) : org = 0xADBD0000, len = 32k
|
||||
ram_external_app_tetris(rwx) : org = 0xADBE0000, len = 32k
|
||||
ram_external_app_extsensors(rwx) : org = 0xADBF0000, len = 32k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -132,4 +133,12 @@ SECTIONS
|
||||
*(*ui*external_app*tetris*);
|
||||
} > ram_external_app_tetris
|
||||
|
||||
.external_app_extsensors : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_extsensors.application_information));
|
||||
*(*ui*external_app*extsensors*);
|
||||
} > ram_external_app_extsensors
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Bernd Herzog
|
||||
*
|
||||
* 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.hpp"
|
||||
#include "ui_extsensors.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::extsensors {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<ExtSensorsView>();
|
||||
}
|
||||
} // namespace ui::external_app::extsensors
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_extsensors.application_information"), used)) application_information_t _application_information_extsensors = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::extsensors::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "ExtSensor",
|
||||
/*.bitmap_data = */ {
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x04,
|
||||
0x20,
|
||||
0x12,
|
||||
0x48,
|
||||
0x8A,
|
||||
0x51,
|
||||
0xCA,
|
||||
0x53,
|
||||
0xCA,
|
||||
0x53,
|
||||
0x8A,
|
||||
0x51,
|
||||
0x12,
|
||||
0x48,
|
||||
0x84,
|
||||
0x21,
|
||||
0xC0,
|
||||
0x03,
|
||||
0x40,
|
||||
0x02,
|
||||
0x60,
|
||||
0x06,
|
||||
0x20,
|
||||
0x04,
|
||||
0x30,
|
||||
0x0C,
|
||||
0xF0,
|
||||
0x0F,
|
||||
},
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::DEBUG,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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_extsensors.hpp"
|
||||
|
||||
#include "rtc_time.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::extsensors {
|
||||
|
||||
void ExtSensorsView::focus() {
|
||||
text_info.focus();
|
||||
}
|
||||
|
||||
ExtSensorsView::ExtSensorsView(NavigationView& nav)
|
||||
: nav_{nav} {
|
||||
add_children({&labels,
|
||||
&text_info,
|
||||
&text_gps,
|
||||
&text_orientation,
|
||||
&text_envl1,
|
||||
&text_envl2});
|
||||
}
|
||||
|
||||
ExtSensorsView::~ExtSensorsView() {
|
||||
}
|
||||
|
||||
void ExtSensorsView::on_any() {
|
||||
if (has_data == false) {
|
||||
// update text
|
||||
text_info.set("Found ext module."); // todo do an ext module check for type
|
||||
}
|
||||
has_data = true;
|
||||
}
|
||||
|
||||
void ExtSensorsView::on_gps(const GPSPosDataMessage* msg) {
|
||||
on_any();
|
||||
std::string tmp = to_string_decimal(msg->lat, 5);
|
||||
tmp += "; ";
|
||||
tmp += to_string_decimal(msg->lon, 5);
|
||||
text_gps.set(tmp);
|
||||
}
|
||||
|
||||
void ExtSensorsView::on_orientation(const OrientationDataMessage* msg) {
|
||||
on_any();
|
||||
std::string tmp = to_string_dec_uint(msg->angle);
|
||||
tmp += (char)176; // °
|
||||
if (msg->tilt < 400) {
|
||||
tmp += "; T: " + to_string_dec_int(msg->tilt);
|
||||
}
|
||||
text_orientation.set(tmp);
|
||||
}
|
||||
|
||||
void ExtSensorsView::on_environment(const EnvironmentDataMessage* msg) {
|
||||
on_any();
|
||||
std::string tmp = "T: " + to_string_decimal(msg->temperature, 2); // temperature
|
||||
tmp += (char)176; // °
|
||||
tmp += "C";
|
||||
tmp += "; H: " + to_string_decimal(msg->humidity, 1) + "%"; // humidity
|
||||
text_envl1.set(tmp);
|
||||
tmp = "P: " + to_string_decimal(msg->pressure, 1) + " hPa; L:"; // pressure
|
||||
tmp += to_string_dec_int(msg->light) + " LUX"; // light
|
||||
text_envl2.set(tmp);
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::extsensors
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
// Code from https://github.com/Flipper-XFW/Xtreme-Apps/tree/04c3a60093e2c2378e79498b4505aa8072980a42/ble_spam/protocols
|
||||
// Thanks for the work of the original creators!
|
||||
// Saying thanks in the main view!
|
||||
|
||||
#ifndef __UI_EXTSENSORS_H__
|
||||
#define __UI_EXTSENSORS_H__
|
||||
|
||||
#include "ui.hpp"
|
||||
#include "ui_language.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_record_view.hpp"
|
||||
#include "app_settings.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::extsensors {
|
||||
|
||||
class ExtSensorsView : public View {
|
||||
public:
|
||||
ExtSensorsView(NavigationView& nav);
|
||||
~ExtSensorsView();
|
||||
|
||||
void focus() override;
|
||||
|
||||
std::string title() const override {
|
||||
return "ExtSensor";
|
||||
};
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
bool has_data = false;
|
||||
|
||||
Labels labels{
|
||||
{{0 * 8, 3 * 16}, "GPS:", Color::light_grey()},
|
||||
{{0 * 8, 5 * 16}, "ORI:", Color::light_grey()},
|
||||
{{0 * 8, 7 * 16}, "ENV:", Color::light_grey()}};
|
||||
|
||||
Text text_info{{0 * 8, 0 * 8, 30 * 8, 16 * 1}, "Connect a compatible module..."};
|
||||
Text text_gps{{5 * 8, 3 * 16, 24 * 8, 16}, "-"};
|
||||
Text text_orientation{{5 * 8, 5 * 16, 24 * 8, 16}, "-"};
|
||||
Text text_envl1{{5 * 8, 7 * 16, 24 * 8, 16}, "-"};
|
||||
Text text_envl2{{1 * 8, 9 * 16, 24 * 8, 16}, "-"};
|
||||
|
||||
void on_any();
|
||||
|
||||
void on_gps(const GPSPosDataMessage* msg);
|
||||
void on_orientation(const OrientationDataMessage* msg);
|
||||
void on_environment(const EnvironmentDataMessage* msg);
|
||||
|
||||
MessageHandlerRegistration message_handler_gps{
|
||||
Message::ID::GPSPosData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const GPSPosDataMessage*>(p);
|
||||
this->on_gps(message);
|
||||
}};
|
||||
MessageHandlerRegistration message_handler_orientation{
|
||||
Message::ID::OrientationData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const OrientationDataMessage*>(p);
|
||||
this->on_orientation(message);
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_environment{
|
||||
Message::ID::EnvironmentData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const EnvironmentDataMessage*>(p);
|
||||
this->on_environment(message);
|
||||
}};
|
||||
};
|
||||
}; // namespace ui::external_app::extsensors
|
||||
|
||||
#endif /*__UI_EXTSENSORS_H__*/
|
||||
+1
-1
@@ -76,7 +76,7 @@ __attribute__((section(".external_app.app_font_viewer.application_information"),
|
||||
/*.icon_color = */ ui::Color::cyan().v,
|
||||
/*.menu_location = */ app_location_t::DEBUG,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_noop */ {'\0', '\0', '\0', '\0'},
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ __attribute__((section(".external_app.app_pacman.application_information"), used
|
||||
/*.icon_color = */ ui::Color::yellow().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_noop */ {'\0', '\0', '\0', '\0'}, // optional
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
|
||||
+62
-17
@@ -43,6 +43,16 @@ boolean but_DOWN = false; // 50
|
||||
boolean but_LEFT = false; // 48
|
||||
boolean but_RIGHT = false; // 46
|
||||
|
||||
enum cheat_code_keymap {
|
||||
UP,
|
||||
DOWN,
|
||||
LEFT,
|
||||
RIGHT
|
||||
};
|
||||
|
||||
enum cheat_code_keymap cheat_code[] = {UP, DOWN, UP, DOWN, RIGHT, LEFT, RIGHT, LEFT};
|
||||
byte cheat_code_index = 0;
|
||||
|
||||
/******************************************************************************/
|
||||
/* GAME VARIABLES AND DEFINITIONS */
|
||||
/******************************************************************************/
|
||||
@@ -1280,29 +1290,64 @@ class Playfield {
|
||||
but_A = false;
|
||||
GAMEPAUSED = 1;
|
||||
} else if (but_LEFT && DEMO == 1 && GAMEPAUSED == 0) { // -level
|
||||
cheat_level = true;
|
||||
|
||||
if ((cheat_code[cheat_code_index] == 2) && (cheat_code_index <= 8)) {
|
||||
cheat_code_index++;
|
||||
}
|
||||
|
||||
if (cheat_code_index == 8) {
|
||||
cheat_level = true;
|
||||
if (LEVEL > 1) {
|
||||
LEVEL--;
|
||||
}
|
||||
Init();
|
||||
}
|
||||
|
||||
but_LEFT = false;
|
||||
if (LEVEL > 1) {
|
||||
LEVEL--;
|
||||
}
|
||||
Init();
|
||||
|
||||
} else if (but_RIGHT && DEMO == 1 && GAMEPAUSED == 0) { // +level
|
||||
cheat_level = true;
|
||||
but_RIGHT = false;
|
||||
if (LEVEL < 255) {
|
||||
LEVEL++;
|
||||
|
||||
if ((cheat_code[cheat_code_index] == 3) && (cheat_code_index <= 8)) {
|
||||
cheat_code_index++;
|
||||
}
|
||||
Init();
|
||||
|
||||
if (cheat_code_index == 8) {
|
||||
cheat_level = true;
|
||||
if (LEVEL < 255) {
|
||||
LEVEL++;
|
||||
}
|
||||
Init();
|
||||
}
|
||||
|
||||
but_RIGHT = false;
|
||||
|
||||
} else if (but_UP && DEMO == 1 && GAMEPAUSED == 0) { // full of lifes
|
||||
cheat_lifes = true;
|
||||
|
||||
if ((cheat_code[cheat_code_index] == 0) && (cheat_code_index <= 8)) {
|
||||
cheat_code_index++;
|
||||
}
|
||||
|
||||
if (cheat_code_index == 8) {
|
||||
cheat_lifes = true;
|
||||
Init();
|
||||
}
|
||||
|
||||
but_UP = false;
|
||||
Init();
|
||||
|
||||
} else if (but_DOWN && DEMO == 1 && GAMEPAUSED == 0) { // reset
|
||||
cheat_level = false;
|
||||
cheat_lifes = false;
|
||||
but_DOWN = false;
|
||||
LIFES = START_LIFES;
|
||||
Init();
|
||||
|
||||
if ((cheat_code[cheat_code_index] == 1) && (cheat_code_index <= 8)) {
|
||||
cheat_code_index++;
|
||||
}
|
||||
|
||||
if (cheat_code_index == 8) {
|
||||
cheat_level = false;
|
||||
cheat_lifes = false;
|
||||
but_DOWN = false;
|
||||
LIFES = START_LIFES;
|
||||
cheat_code_index = 0;
|
||||
Init();
|
||||
}
|
||||
}
|
||||
|
||||
if (GAMEPAUSED && but_A && DEMO == 0) {
|
||||
|
||||
+2
-2
@@ -76,7 +76,7 @@ __attribute__((section(".external_app.app_tetris.application_information"), used
|
||||
/*.icon_color = */ ui::Color::orange().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_noop */ {'\0', '\0', '\0', '\0'}, // optional
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
|
||||
@@ -461,6 +461,15 @@ std::string path::string() const {
|
||||
return conv.to_bytes(native());
|
||||
}
|
||||
|
||||
// appends a string to the end of filename, but leaves the extension asd.txt + "fg" -> asdfg.txt
|
||||
path& path::append_filename(const string_type& str) {
|
||||
const auto t = extension().native();
|
||||
_s.erase(_s.size() - t.size()); // remove extension
|
||||
_s += str; // append string
|
||||
_s += t; // add back extension
|
||||
return *this;
|
||||
}
|
||||
|
||||
path& path::replace_extension(const path& replacement) {
|
||||
const auto t = extension().native();
|
||||
_s.erase(_s.size() - t.size());
|
||||
|
||||
@@ -155,6 +155,8 @@ struct path {
|
||||
|
||||
path& replace_extension(const path& replacement = path());
|
||||
|
||||
path& append_filename(const string_type& str);
|
||||
|
||||
private:
|
||||
string_type _s;
|
||||
};
|
||||
|
||||
@@ -376,7 +376,7 @@ bool parse_freqman_entry(std::string_view str, freqman_entry& entry) {
|
||||
} else if (key == "c") {
|
||||
entry.tone = parse_tone_key(value);
|
||||
} else if (key == "d") {
|
||||
entry.description = trim(value);
|
||||
entry.description = trim(value).substr(0, freqman_max_desc_size);
|
||||
} else if (key == "f") {
|
||||
entry.type = freqman_type::Single;
|
||||
parse_int(value, entry.frequency_a);
|
||||
@@ -492,7 +492,7 @@ freqman_entry FreqmanDB::operator[](Index index) const {
|
||||
return entry;
|
||||
else if (read_raw_) {
|
||||
entry.type = freqman_type::Raw;
|
||||
entry.description = trim(*line_text);
|
||||
entry.description = trim(*line_text).substr(0, freqman_max_desc_size);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,6 +162,9 @@ std::string freqman_entry_get_step_string_short(freqman_index_t step);
|
||||
* ensure app memory stability. */
|
||||
constexpr size_t freqman_default_max_entries = 150;
|
||||
|
||||
/* Limiting description to 30 as specified by the format */
|
||||
constexpr size_t freqman_max_desc_size = 30;
|
||||
|
||||
struct freqman_load_options {
|
||||
/* Loads all entries when set to 0. */
|
||||
size_t max_entries{freqman_default_max_entries};
|
||||
|
||||
@@ -150,7 +150,9 @@ void MAX2837::init() {
|
||||
}
|
||||
|
||||
void MAX2837::set_tx_LO_iq_phase_calibration(const size_t v) {
|
||||
/* IQ phase deg CAL adj (+4 ...-4) in 32 steps (5 bits), 00000 = +4deg (Q lags I by 94degs, default), 01111 = +0deg, 11111 = -4deg (Q lags I by 86degs) */
|
||||
Mode saved_mode = get_mode();
|
||||
|
||||
/* TX IQ phase deg CAL adj (+4 ...-4) in 32 steps (5 bits), 00000 = +4deg (Q lags I by 94degs, default), 01111 = +0deg, 11111 = -4deg (Q lags I by 86degs) */
|
||||
|
||||
// TX calibration , Logic pins , ENABLE, RXENABLE, TXENABLE = 1,0,1 (5dec), and Reg address 16, D1 (CAL mode 1):DO (CHIP ENABLE 1)
|
||||
set_mode(Mode::Tx_Calibration); // write to ram 3 LOGIC Pins .
|
||||
@@ -172,7 +174,7 @@ void MAX2837::set_tx_LO_iq_phase_calibration(const size_t v) {
|
||||
_map.r.spi_en.EN_SPI = 1; // Register Settings reg address 16, DO (1 = Chip select enable )
|
||||
flush_one(Register::SPI_EN);
|
||||
|
||||
set_mode(Mode::Standby); // Back 3 logic pins CALIBRATION mode -> Standby.
|
||||
set_mode(saved_mode); // restore original mode
|
||||
}
|
||||
|
||||
enum class Mask { // There are class Mask ,and class mode with same names, but they are not same.
|
||||
@@ -205,12 +207,18 @@ Mask mode_mask(const Mode mode) { // based on enum Mode cases, we set up the co
|
||||
}
|
||||
|
||||
void MAX2837::set_mode(const Mode mode) { // We set up the 3 Logic Pins ENABLE, RXENABLE, TXENABLE accordingly to the max2837 mode case, that we want to set up .
|
||||
_mode = mode;
|
||||
|
||||
Mask mask = mode_mask(mode);
|
||||
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
|
||||
gpio_max2837_rxenable.write(toUType(mask) & toUType(Mask::RxEnable));
|
||||
gpio_max2837_txenable.write(toUType(mask) & toUType(Mask::TxEnable));
|
||||
}
|
||||
|
||||
Mode MAX2837::get_mode() {
|
||||
return _mode;
|
||||
}
|
||||
|
||||
void MAX2837::flush() {
|
||||
if (_dirty) {
|
||||
for (size_t n = 0; n < reg_count; n++) {
|
||||
@@ -324,14 +332,41 @@ bool MAX2837::set_frequency(const rf::Frequency lo_frequency) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MAX2837::set_rx_lo_iq_calibration(const size_t v) {
|
||||
/*
|
||||
void MAX2837::set_rx_lo_iq_calibration(const size_t v) { // Original code , rewritten below
|
||||
_map.r.rx_top_rx_bias.RX_IQERR_SPI_EN = 1;
|
||||
_dirty[Register::RX_TOP_RX_BIAS] = 1;
|
||||
_map.r.rxrf_2.iqerr_trim = v;
|
||||
_dirty[Register::RXRF_2] = 1;
|
||||
flush();
|
||||
}
|
||||
*/
|
||||
|
||||
void MAX2837::set_rx_LO_iq_phase_calibration(const size_t v) {
|
||||
/* RX IQ phase deg CAL adj (+4 ...-4) in 32 steps (5 bits), 00000 = +4deg (Q lags I by 94degs, default), 01111 = +0deg, 11111 = -4deg (Q lags I by 86degs) */
|
||||
|
||||
// RX calibration , Logic pins , ENABLE, RXENABLE, TXENABLE = 1,1,0 (3dec), and Reg address 16, D1 (CAL mode 1):DO (CHIP ENABLE 1)
|
||||
set_mode(Mode::Rx_Calibration); // write to ram 3 LOGIC Pins .
|
||||
|
||||
gpio_max283x_enable.output();
|
||||
gpio_max2837_rxenable.output();
|
||||
gpio_max2837_txenable.output();
|
||||
|
||||
_map.r.spi_en.CAL_SPI = 1; // Register Settings reg address 16, D1 (CAL mode 1)
|
||||
_map.r.spi_en.EN_SPI = 1; // Register Settings reg address 16, DO (CHIP ENABLE 1)
|
||||
flush_one(Register::SPI_EN);
|
||||
|
||||
_map.r.rx_top_rx_bias.RX_IQERR_SPI_EN = 1; // reg 8 D9, RX LO IQ Phase calibration SPI control. Active when Address 8 D<9> = 1.
|
||||
flush_one(Register::RX_TOP_RX_BIAS);
|
||||
|
||||
_map.r.rxrf_2.iqerr_trim = v; // reg 1 D9:D5, RX LO I/Q Phase SPI 5 bits Adjust
|
||||
flush_one(Register::RXRF_2);
|
||||
|
||||
// Exit Calibration mode, Go back to reg 16, D1:D0 , Out of CALIBRATION , back to default conditions, but keep CS activated.
|
||||
_map.r.spi_en.CAL_SPI = 0; // Register Settings reg address 16, D1 (0 = Normal operation (default)
|
||||
_map.r.spi_en.EN_SPI = 1; // Register Settings reg address 16, DO (1 = Chip select enable )
|
||||
flush_one(Register::SPI_EN);
|
||||
}
|
||||
|
||||
void MAX2837::set_rx_bias_trim(const size_t v) {
|
||||
_map.r.rx_top_rx_bias.EN_Bias_Trim = 1;
|
||||
|
||||
@@ -828,7 +828,7 @@ class MAX2837 : public MAX283x {
|
||||
|
||||
bool set_frequency(const rf::Frequency lo_frequency) override;
|
||||
|
||||
void set_rx_lo_iq_calibration(const size_t v) override;
|
||||
void set_rx_LO_iq_phase_calibration(const size_t v) override;
|
||||
void set_tx_LO_iq_phase_calibration(const size_t v) override;
|
||||
void set_rx_bias_trim(const size_t v);
|
||||
void set_vco_bias(const size_t v);
|
||||
@@ -841,6 +841,7 @@ class MAX2837 : public MAX283x {
|
||||
|
||||
private:
|
||||
spi::arbiter::Target& _target;
|
||||
Mode _mode{Mode::Standby};
|
||||
|
||||
RegisterMap _map{initial_register_values};
|
||||
DirtyRegisters<Register, reg_count> _dirty{};
|
||||
@@ -851,6 +852,7 @@ class MAX2837 : public MAX283x {
|
||||
reg_t read(const Register reg);
|
||||
|
||||
void flush();
|
||||
Mode get_mode();
|
||||
};
|
||||
|
||||
} // namespace max2837
|
||||
|
||||
@@ -143,6 +143,8 @@ void MAX2839::init() {
|
||||
}
|
||||
|
||||
void MAX2839::set_tx_LO_iq_phase_calibration(const size_t v) {
|
||||
Mode saved_mode = get_mode();
|
||||
|
||||
/* IQ phase deg CAL adj (+4 ...-4) This IC in 64 steps (6 bits), 000000 = +4deg (Q lags I by 94degs, default), 011111 = +0deg, 111111 = -4deg (Q lags I by 86degs) */
|
||||
|
||||
// TX calibration , 2 x Logic pins , ENABLE, RXENABLE = 1,0, (2dec), and Reg address 16, D1 (CAL mode 1):DO (CHIP ENABLE 1)
|
||||
@@ -164,7 +166,7 @@ void MAX2839::set_tx_LO_iq_phase_calibration(const size_t v) {
|
||||
_map.r.spi_en.EN_SPI = 1; // Register Settings reg address 16, DO (1 = Chip select enable )
|
||||
flush_one(Register::SPI_EN);
|
||||
|
||||
set_mode(Mode::Standby); // Back 3 logic pins CALIBRATION mode -> Standby.
|
||||
set_mode(saved_mode); // restore original mode
|
||||
}
|
||||
|
||||
enum class Mask {
|
||||
@@ -196,11 +198,17 @@ Mask mode_mask(const Mode mode) {
|
||||
}
|
||||
|
||||
void MAX2839::set_mode(const Mode mode) {
|
||||
_mode = mode;
|
||||
|
||||
Mask mask = mode_mask(mode);
|
||||
gpio_max283x_enable.write(toUType(mask) & toUType(Mask::Enable));
|
||||
gpio_max2839_rxtx.write(toUType(mask) & toUType(Mask::RxTx));
|
||||
}
|
||||
|
||||
Mode MAX2839::get_mode() {
|
||||
return _mode;
|
||||
}
|
||||
|
||||
void MAX2839::flush() {
|
||||
if (_dirty) {
|
||||
for (size_t n = 0; n < reg_count; n++) {
|
||||
@@ -367,13 +375,38 @@ bool MAX2839::set_frequency(const rf::Frequency lo_frequency) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MAX2839::set_rx_lo_iq_calibration(const size_t v) {
|
||||
/*
|
||||
void MAX2839::set_rx_LO_iq_phase_calibration(const size_t v) { // Original code , rewritten below
|
||||
_map.r.rxrf_2.RX_IQERR_SPI_EN = 1;
|
||||
_dirty[Register::RXRF_2] = 1;
|
||||
_map.r.rxrf_1.iqerr_trim = v;
|
||||
_dirty[Register::RXRF_1] = 1;
|
||||
flush();
|
||||
}*/
|
||||
|
||||
void MAX2839::set_rx_LO_iq_phase_calibration(const size_t v) {
|
||||
/* RX IQ phase deg CAL adj (+4 ...-4) in 64 steps (6 bits), 000000 = +4deg (Q lags I by 94degs, default), 011111 = +0deg, 111111 = -4deg (Q lags I by 86degs) */
|
||||
|
||||
// RX calibration , Logic pins , ENABLE, RXENABLE, TXENABLE = 1,1,0 (3dec), and Reg address 16, D1 (CAL mode 1):DO (CHIP ENABLE 1)
|
||||
set_mode(Mode::Rx_Calibration); // write to ram 3 LOGIC Pins .
|
||||
|
||||
gpio_max283x_enable.output(); // max2839 has only 2 x pins + regs to decide mode.
|
||||
gpio_max2839_rxtx.output(); // Here is combined rx & tx pin in one port.
|
||||
|
||||
_map.r.spi_en.CAL_SPI = 1; // Register Settings reg address 16, D1 (CAL mode 1)
|
||||
_map.r.spi_en.EN_SPI = 1; // Register Settings reg address 16, DO (CHIP ENABLE 1)
|
||||
flush_one(Register::SPI_EN);
|
||||
|
||||
_map.r.rxrf_2.RX_IQERR_SPI_EN = 1; // reg 2 D<2> = 1, RX LO IQ calibration SPI control. Active when Address 2 D<2> = 1.
|
||||
_dirty[Register::RXRF_2] = 1;
|
||||
|
||||
_map.r.rxrf_1.iqerr_trim = v;
|
||||
_dirty[Register::RXRF_1] = 1;
|
||||
flush();
|
||||
|
||||
_map.r.spi_en.CAL_SPI = 0; // Register Settings reg address 16, D1 (CAL mode 1)
|
||||
_map.r.spi_en.EN_SPI = 1; // Register Settings reg address 16, DO (CHIP ENABLE 1)
|
||||
flush_one(Register::SPI_EN);
|
||||
}
|
||||
|
||||
void MAX2839::set_rx_buff_vcm(const size_t v) {
|
||||
|
||||
@@ -82,11 +82,11 @@ struct RXENABLE_Type {
|
||||
static_assert(sizeof(RXENABLE_Type) == sizeof(reg_t), "RXENABLE_Type wrong size");
|
||||
|
||||
struct RXRF_1_Type {
|
||||
reg_t LNAband : 1;
|
||||
reg_t LNAband : 2; // Datasheet says D1:D0 , 2 bits, maybe D1 Rx_B, D0 Rx_A , (original code said wrongly ,reg_t LNAband : 1; that was of for max2837, not max2839 )
|
||||
reg_t RESERVED0 : 1;
|
||||
reg_t MIMOmode : 1;
|
||||
reg_t iqerr_trim : 5;
|
||||
reg_t RESERVED1 : 6;
|
||||
reg_t iqerr_trim : 6; // Datasheet says D9_D4 , that means 6 bits, (original code said wrongly ,reg_t iqerr_trim : 5; )
|
||||
reg_t RESERVED1 : 6; // we are using 16 bits , even top part mapping is not used
|
||||
};
|
||||
|
||||
static_assert(sizeof(RXRF_1_Type) == sizeof(reg_t), "RXRF_1_Type wrong size");
|
||||
@@ -689,7 +689,7 @@ class MAX2839 : public MAX283x {
|
||||
void set_lpf_rf_bandwidth_rx(const uint32_t bandwidth_minimum) override;
|
||||
void set_lpf_rf_bandwidth_tx(const uint32_t bandwidth_minimum) override;
|
||||
bool set_frequency(const rf::Frequency lo_frequency) override;
|
||||
void set_rx_lo_iq_calibration(const size_t v) override;
|
||||
void set_rx_LO_iq_phase_calibration(const size_t v) override;
|
||||
void set_tx_LO_iq_phase_calibration(const size_t v) override;
|
||||
void set_rx_buff_vcm(const size_t v) override;
|
||||
|
||||
@@ -700,6 +700,7 @@ class MAX2839 : public MAX283x {
|
||||
|
||||
private:
|
||||
spi::arbiter::Target& _target;
|
||||
Mode _mode{Mode::Standby};
|
||||
|
||||
RegisterMap _map{initial_register_values};
|
||||
DirtyRegisters<Register, reg_count> _dirty{};
|
||||
@@ -712,6 +713,7 @@ class MAX2839 : public MAX283x {
|
||||
void flush();
|
||||
|
||||
void configure_rx_gain();
|
||||
Mode get_mode();
|
||||
};
|
||||
|
||||
} // namespace max2839
|
||||
|
||||
@@ -125,7 +125,7 @@ class MAX283x {
|
||||
|
||||
virtual bool set_frequency(const rf::Frequency lo_frequency);
|
||||
|
||||
virtual void set_rx_lo_iq_calibration(const size_t v);
|
||||
virtual void set_rx_LO_iq_phase_calibration(const size_t v);
|
||||
virtual void set_tx_LO_iq_phase_calibration(const size_t v);
|
||||
|
||||
virtual void set_rx_buff_vcm(const size_t v);
|
||||
|
||||
@@ -31,6 +31,9 @@ using namespace std::literals;
|
||||
|
||||
const std::string_view center_freq_name = "center_frequency"sv;
|
||||
const std::string_view sample_rate_name = "sample_rate"sv;
|
||||
const std::string_view latitude_name = "latitude"sv;
|
||||
const std::string_view longitude_name = "longitude"sv;
|
||||
const std::string_view satinuse_name = "satinuse"sv;
|
||||
|
||||
fs::path get_metadata_path(const fs::path& capture_path) {
|
||||
auto temp = capture_path;
|
||||
@@ -54,6 +57,23 @@ Optional<File::Error> write_metadata_file(const fs::path& path, capture_metadata
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
// add gps data if available
|
||||
if (metadata.latitude != 0 && metadata.longitude != 0 && metadata.latitude < 200 && metadata.longitude < 200) {
|
||||
error = f.write_line(std::string{latitude_name} + "=" +
|
||||
to_string_decimal(metadata.latitude, 7));
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = f.write_line(std::string{longitude_name} + "=" +
|
||||
to_string_decimal(metadata.longitude, 7));
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = f.write_line(std::string{satinuse_name} + "=" +
|
||||
to_string_dec_uint(metadata.satinuse));
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -77,6 +97,12 @@ Optional<capture_metadata> read_metadata_file(const fs::path& path) {
|
||||
parse_int(cols[1], metadata.center_frequency);
|
||||
else if (cols[0] == sample_rate_name)
|
||||
parse_int(cols[1], metadata.sample_rate);
|
||||
else if (cols[0] == latitude_name)
|
||||
parse_float_meta(cols[1], metadata.latitude);
|
||||
else if (cols[0] == longitude_name)
|
||||
parse_float_meta(cols[1], metadata.longitude);
|
||||
else if (cols[0] == satinuse_name)
|
||||
parse_int(cols[1], metadata.satinuse);
|
||||
else
|
||||
continue;
|
||||
}
|
||||
@@ -86,3 +112,18 @@ Optional<capture_metadata> read_metadata_file(const fs::path& path) {
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
bool parse_float_meta(std::string_view str, float& out_val) {
|
||||
out_val = {};
|
||||
|
||||
if (str.size() > max_parse_int_length)
|
||||
return false;
|
||||
|
||||
// Copy onto the stack and null terminate.
|
||||
char zstr[max_parse_int_length + 1];
|
||||
std::memcpy(zstr, str.data(), str.size());
|
||||
zstr[str.size()] = '\0';
|
||||
errno = 0;
|
||||
out_val = strtod(zstr, nullptr);
|
||||
return (errno == 0);
|
||||
}
|
||||
@@ -29,6 +29,9 @@
|
||||
struct capture_metadata {
|
||||
rf::Frequency center_frequency;
|
||||
uint32_t sample_rate;
|
||||
float latitude = 0;
|
||||
float longitude = 0;
|
||||
uint8_t satinuse = 0;
|
||||
};
|
||||
|
||||
std::filesystem::path get_metadata_path(const std::filesystem::path& capture_path);
|
||||
@@ -36,4 +39,5 @@ std::filesystem::path get_metadata_path(const std::filesystem::path& capture_pat
|
||||
Optional<File::Error> write_metadata_file(const std::filesystem::path& path, capture_metadata metadata);
|
||||
Optional<capture_metadata> read_metadata_file(const std::filesystem::path& path);
|
||||
|
||||
bool parse_float_meta(std::string_view str, float& out_val);
|
||||
#endif // __METADATA_FILE_HPP__
|
||||
@@ -253,6 +253,10 @@ void set_tx_max283x_iq_phase_calibration(const size_t v) {
|
||||
second_if->set_tx_LO_iq_phase_calibration(v);
|
||||
}
|
||||
|
||||
void set_rx_max283x_iq_phase_calibration(const size_t v) {
|
||||
second_if->set_rx_LO_iq_phase_calibration(v);
|
||||
}
|
||||
|
||||
/*void enable(Configuration configuration) {
|
||||
configure(configuration);
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ void set_baseband_filter_bandwidth_tx(const uint32_t bandwidth_minimum);
|
||||
void set_baseband_rate(const uint32_t rate);
|
||||
void set_antenna_bias(const bool on);
|
||||
void set_tx_max283x_iq_phase_calibration(const size_t v);
|
||||
void set_rx_max283x_iq_phase_calibration(const size_t v);
|
||||
|
||||
/* Use ReceiverModel or TransmitterModel instead. */
|
||||
// void enable(Configuration configuration);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2019 Elia Yehuda (z4ziggy)
|
||||
* Copyright (C) 2023 Mark Thompson
|
||||
* Copyright (C) 2024 u-foka
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -106,7 +107,14 @@ void BtnGridView::on_tick_second() {
|
||||
}
|
||||
|
||||
void BtnGridView::clear() {
|
||||
// clear vector and release memory, not using swap since it's causing capture to glitch/fault
|
||||
menu_items.clear();
|
||||
|
||||
for (auto& item : menu_item_views)
|
||||
remove_child(item.get());
|
||||
|
||||
// clear vector and release memory, not using swap since it's causing capture to glitch/fault
|
||||
menu_item_views.clear();
|
||||
}
|
||||
|
||||
void BtnGridView::add_items(std::initializer_list<GridItem> new_items) {
|
||||
@@ -207,11 +215,23 @@ void BtnGridView::on_focus() {
|
||||
|
||||
void BtnGridView::on_blur() {
|
||||
#if 0
|
||||
if (!keep_highlight)
|
||||
item_view(highlighted_item - offset)->unhighlight();
|
||||
if (!keep_highlight)
|
||||
item_view(highlighted_item - offset)->unhighlight();
|
||||
#endif
|
||||
}
|
||||
|
||||
void BtnGridView::on_show() {
|
||||
on_populate();
|
||||
|
||||
View::on_show();
|
||||
}
|
||||
|
||||
void BtnGridView::on_hide() {
|
||||
View::on_hide();
|
||||
|
||||
clear();
|
||||
}
|
||||
|
||||
bool BtnGridView::on_key(const KeyEvent key) {
|
||||
switch (key) {
|
||||
case KeyEvent::Up:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
* Copyright (C) 2019 Elia Yehuda (z4ziggy)
|
||||
* Copyright (C) 2024 u-foka
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -73,10 +74,15 @@ class BtnGridView : public View {
|
||||
void set_arrow_enabled(bool enabled);
|
||||
void on_focus() override;
|
||||
void on_blur() override;
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
bool on_key(const KeyEvent event) override;
|
||||
bool on_encoder(const EncoderEvent event) override;
|
||||
bool blacklisted_app(GridItem new_item);
|
||||
|
||||
protected:
|
||||
virtual void on_populate() = 0;
|
||||
|
||||
private:
|
||||
int rows_{3};
|
||||
void update_items();
|
||||
|
||||
@@ -163,19 +163,19 @@ void RSSI::paint(Painter& painter) {
|
||||
}
|
||||
}
|
||||
|
||||
int16_t RSSI::get_min() {
|
||||
uint8_t RSSI::get_min() {
|
||||
return min_;
|
||||
}
|
||||
|
||||
int16_t RSSI::get_avg() {
|
||||
uint8_t RSSI::get_avg() {
|
||||
return avg_;
|
||||
}
|
||||
|
||||
int16_t RSSI::get_max() {
|
||||
uint8_t RSSI::get_max() {
|
||||
return max_;
|
||||
}
|
||||
|
||||
int16_t RSSI::get_delta() {
|
||||
uint8_t RSSI::get_delta() {
|
||||
return max_ - min_;
|
||||
}
|
||||
|
||||
@@ -213,19 +213,19 @@ void RSSI::on_statistics_update(const RSSIStatistics& statistics) {
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
int16_t RSSIGraph::get_graph_min() {
|
||||
uint8_t RSSIGraph::get_graph_min() {
|
||||
return graph_min_;
|
||||
}
|
||||
|
||||
int16_t RSSIGraph::get_graph_avg() {
|
||||
uint8_t RSSIGraph::get_graph_avg() {
|
||||
return graph_avg_;
|
||||
}
|
||||
|
||||
int16_t RSSIGraph::get_graph_max() {
|
||||
uint8_t RSSIGraph::get_graph_max() {
|
||||
return graph_max_;
|
||||
}
|
||||
|
||||
int16_t RSSIGraph::get_graph_delta() {
|
||||
uint8_t RSSIGraph::get_graph_delta() {
|
||||
return graph_max_ - graph_min_;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,10 +50,10 @@ class RSSI : public Widget {
|
||||
}
|
||||
|
||||
// get last used/received min/avg/max/delta
|
||||
int16_t get_min();
|
||||
int16_t get_avg();
|
||||
int16_t get_max();
|
||||
int16_t get_delta();
|
||||
uint8_t get_min();
|
||||
uint8_t get_avg();
|
||||
uint8_t get_max();
|
||||
uint8_t get_delta();
|
||||
void set_vertical_rssi(bool enabled);
|
||||
void set_peak(bool enabled, size_t duration);
|
||||
|
||||
@@ -63,10 +63,10 @@ class RSSI : public Widget {
|
||||
bool on_touch(const TouchEvent event) override;
|
||||
|
||||
private:
|
||||
int16_t min_ = 0;
|
||||
int16_t avg_ = 0;
|
||||
int16_t max_ = 0;
|
||||
int16_t peak_ = 0;
|
||||
int8_t min_ = 0;
|
||||
int8_t avg_ = 0;
|
||||
int8_t max_ = 0;
|
||||
int8_t peak_ = 0;
|
||||
size_t peak_duration_ = 0;
|
||||
bool instant_exec_{false};
|
||||
|
||||
@@ -115,10 +115,10 @@ class RSSIGraph : public Widget {
|
||||
void on_hide() override;
|
||||
void on_show() override;
|
||||
// get whole graph_list min/avg/max/delta
|
||||
int16_t get_graph_min();
|
||||
int16_t get_graph_avg();
|
||||
int16_t get_graph_max();
|
||||
int16_t get_graph_delta();
|
||||
uint8_t get_graph_min();
|
||||
uint8_t get_graph_avg();
|
||||
uint8_t get_graph_max();
|
||||
uint8_t get_graph_delta();
|
||||
|
||||
private:
|
||||
int16_t graph_min_ = 0;
|
||||
|
||||
@@ -740,40 +740,49 @@ void addExternalItems(NavigationView& nav, app_location_t location, BtnGridView&
|
||||
|
||||
/* ReceiversMenuView *****************************************************/
|
||||
|
||||
ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
|
||||
ReceiversMenuView::ReceiversMenuView(NavigationView& nav)
|
||||
: nav_(nav) {}
|
||||
|
||||
void ReceiversMenuView::on_populate() {
|
||||
if (pmem::show_gui_return_icon()) {
|
||||
add_item({"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }});
|
||||
add_item({"..", Color::light_grey(), &bitmap_icon_previous, [this]() { nav_.pop(); }});
|
||||
}
|
||||
|
||||
add_apps(nav, *this, RX);
|
||||
add_apps(nav_, *this, RX);
|
||||
|
||||
addExternalItems(nav, app_location_t::RX, *this);
|
||||
addExternalItems(nav_, app_location_t::RX, *this);
|
||||
}
|
||||
|
||||
/* TransmittersMenuView **************************************************/
|
||||
|
||||
TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
|
||||
TransmittersMenuView::TransmittersMenuView(NavigationView& nav)
|
||||
: nav_(nav) {}
|
||||
|
||||
void TransmittersMenuView::on_populate() {
|
||||
if (pmem::show_gui_return_icon()) {
|
||||
add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
|
||||
add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [this]() { nav_.pop(); }}});
|
||||
}
|
||||
|
||||
add_apps(nav, *this, TX);
|
||||
add_apps(nav_, *this, TX);
|
||||
|
||||
addExternalItems(nav, app_location_t::TX, *this);
|
||||
addExternalItems(nav_, app_location_t::TX, *this);
|
||||
}
|
||||
|
||||
/* UtilitiesMenuView *****************************************************/
|
||||
|
||||
UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav) {
|
||||
UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
|
||||
void UtilitiesMenuView::on_populate() {
|
||||
if (pmem::show_gui_return_icon()) {
|
||||
add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}});
|
||||
add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [this]() { nav_.pop(); }}});
|
||||
}
|
||||
|
||||
add_apps(nav, *this, UTILITIES);
|
||||
add_apps(nav_, *this, UTILITIES);
|
||||
|
||||
addExternalItems(nav, app_location_t::UTILITIES, *this);
|
||||
|
||||
set_max_rows(2); // allow wider buttons
|
||||
addExternalItems(nav_, app_location_t::UTILITIES, *this);
|
||||
}
|
||||
|
||||
/* SystemMenuView ********************************************************/
|
||||
@@ -790,15 +799,18 @@ void SystemMenuView::hackrf_mode(NavigationView& nav) {
|
||||
});
|
||||
}
|
||||
|
||||
SystemMenuView::SystemMenuView(NavigationView& nav) {
|
||||
add_apps(nav, *this, HOME);
|
||||
|
||||
add_item({"HackRF", Color::cyan(), &bitmap_icon_hackrf, [this, &nav]() { hackrf_mode(nav); }});
|
||||
|
||||
SystemMenuView::SystemMenuView(NavigationView& nav)
|
||||
: nav_(nav) {
|
||||
set_max_rows(2); // allow wider buttons
|
||||
set_arrow_enabled(false);
|
||||
}
|
||||
|
||||
void SystemMenuView::on_populate() {
|
||||
add_apps(nav_, *this, HOME);
|
||||
|
||||
add_item({"HackRF", Color::cyan(), &bitmap_icon_hackrf, [this]() { hackrf_mode(nav_); }});
|
||||
}
|
||||
|
||||
/* SystemView ************************************************************/
|
||||
|
||||
SystemView::SystemView(
|
||||
@@ -861,6 +873,7 @@ NavigationView* SystemView::get_navigation_view() {
|
||||
}
|
||||
|
||||
void SystemView::toggle_overlay() {
|
||||
static uint8_t last_perf_counter_status = shared_memory.request_m4_performance_counter;
|
||||
switch (++overlay_active) {
|
||||
case 1:
|
||||
this->add_child(&this->overlay);
|
||||
@@ -879,7 +892,7 @@ void SystemView::toggle_overlay() {
|
||||
case 3:
|
||||
this->remove_child(&this->overlay2);
|
||||
this->set_dirty();
|
||||
shared_memory.request_m4_performance_counter = 0;
|
||||
shared_memory.request_m4_performance_counter = last_perf_counter_status;
|
||||
overlay_active = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -320,10 +320,6 @@ class BMPView : public View {
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
Text text_info{
|
||||
{4 * 8, 284, 20 * 8, 16},
|
||||
"Version " VERSION_STRING};
|
||||
|
||||
Button button_done{
|
||||
{240, 0, 1, 1},
|
||||
""};
|
||||
@@ -333,18 +329,30 @@ class ReceiversMenuView : public BtnGridView {
|
||||
public:
|
||||
ReceiversMenuView(NavigationView& nav);
|
||||
std::string title() const override { return "Receive"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
void on_populate() override;
|
||||
};
|
||||
|
||||
class TransmittersMenuView : public BtnGridView {
|
||||
public:
|
||||
TransmittersMenuView(NavigationView& nav);
|
||||
std::string title() const override { return "Transmit"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
void on_populate() override;
|
||||
};
|
||||
|
||||
class UtilitiesMenuView : public BtnGridView {
|
||||
public:
|
||||
UtilitiesMenuView(NavigationView& nav);
|
||||
std::string title() const override { return "Utilities"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
void on_populate() override;
|
||||
};
|
||||
|
||||
class SystemMenuView : public BtnGridView {
|
||||
@@ -352,6 +360,8 @@ class SystemMenuView : public BtnGridView {
|
||||
SystemMenuView(NavigationView& nav);
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
void on_populate() override;
|
||||
void hackrf_mode(NavigationView& nav);
|
||||
};
|
||||
|
||||
|
||||
@@ -205,6 +205,11 @@ void RecordView::start() {
|
||||
return;
|
||||
}
|
||||
|
||||
// check for geo data, if present append filename with _GEO
|
||||
if (latitude != 0 && longitude != 0 && latitude < 200 && longitude < 200) {
|
||||
base_path.append_filename(u"_GEO");
|
||||
}
|
||||
|
||||
std::unique_ptr<stream::Writer> writer;
|
||||
switch (file_type) {
|
||||
case FileType::WAV: {
|
||||
@@ -223,7 +228,7 @@ void RecordView::start() {
|
||||
case FileType::RawS8:
|
||||
case FileType::RawS16: {
|
||||
const auto metadata_file_error = write_metadata_file(
|
||||
get_metadata_path(base_path), {receiver_model.target_frequency(), sampling_rate});
|
||||
get_metadata_path(base_path), {receiver_model.target_frequency(), sampling_rate, latitude, longitude, satinuse});
|
||||
if (metadata_file_error.is_valid()) {
|
||||
handle_error(metadata_file_error.value());
|
||||
return;
|
||||
@@ -342,6 +347,12 @@ void RecordView::trim_capture() {
|
||||
trim_path = {};
|
||||
}
|
||||
|
||||
void RecordView::on_gps(const GPSPosDataMessage* msg) {
|
||||
latitude = msg->lat;
|
||||
longitude = msg->lon;
|
||||
satinuse = msg->satinuse;
|
||||
}
|
||||
|
||||
void RecordView::handle_capture_thread_done(const File::Error error) {
|
||||
stop();
|
||||
if (error.code()) {
|
||||
|
||||
@@ -88,6 +88,7 @@ class RecordView : public View {
|
||||
|
||||
OversampleRate get_oversample_rate(uint32_t sample_rate);
|
||||
|
||||
void on_gps(const GPSPosDataMessage* msg);
|
||||
// bool pitch_rssi_enabled = false;
|
||||
|
||||
// Time Stamp
|
||||
@@ -95,6 +96,10 @@ class RecordView : public View {
|
||||
bool filename_as_is = false;
|
||||
rtc::RTC datetime{};
|
||||
|
||||
float latitude = 0; // for wardriwing with ext module
|
||||
float longitude = 0;
|
||||
uint8_t satinuse = 0; // to see if there was enough sats used or not
|
||||
|
||||
const std::filesystem::path filename_stem_pattern;
|
||||
const std::filesystem::path folder;
|
||||
FileType file_type;
|
||||
@@ -147,6 +152,13 @@ class RecordView : public View {
|
||||
const auto message = *reinterpret_cast<const CaptureThreadDoneMessage*>(p);
|
||||
this->handle_capture_thread_done(message.error);
|
||||
}};
|
||||
|
||||
MessageHandlerRegistration message_handler_gps{
|
||||
Message::ID::GPSPosData,
|
||||
[this](Message* const p) {
|
||||
const auto message = static_cast<const GPSPosDataMessage*>(p);
|
||||
this->on_gps(message);
|
||||
}};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -211,6 +211,7 @@ static void cmd_screenframe(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
// calculates the 1 byte rgb value, and add 32 to it, so it can be a printable character.
|
||||
static char getChrFromRgb(uint8_t r, uint8_t g, uint8_t b) {
|
||||
uint8_t chR = r >> 6; // 3bit
|
||||
uint8_t chG = g >> 6; // 3bit
|
||||
@@ -220,26 +221,36 @@ static char getChrFromRgb(uint8_t r, uint8_t g, uint8_t b) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// keep track of a buffer, and sends only if full. not only line by line
|
||||
static void screenbuffer_helper_add(BaseSequentialStream* chp, char* buffer, size_t& wp, char ch) {
|
||||
buffer[wp++] = ch;
|
||||
if (wp > USBSERIAL_BUFFERS_SIZE - 1) {
|
||||
fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, (const uint8_t*)buffer, USBSERIAL_BUFFERS_SIZE);
|
||||
wp = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// sends only 1 byte (printable only) per pixel, so around 96 colors
|
||||
static void cmd_screenframeshort(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
auto evtd = getEventDispatcherInstance();
|
||||
evtd->enter_shell_working_mode();
|
||||
|
||||
char buffer[USBSERIAL_BUFFERS_SIZE];
|
||||
size_t wp = 0;
|
||||
for (int y = 0; y < ui::screen_height; y++) {
|
||||
std::array<ui::ColorRGB888, ui::screen_width> row;
|
||||
portapack::display.read_pixels({0, y, ui::screen_width, 1}, row);
|
||||
char buffer[242];
|
||||
for (int i = 0; i < 240; ++i) {
|
||||
buffer[i] = getChrFromRgb(row[i].r, row[i].g, row[i].b);
|
||||
screenbuffer_helper_add(chp, buffer, wp, getChrFromRgb(row[i].r, row[i].g, row[i].b));
|
||||
}
|
||||
buffer[240] = '\r';
|
||||
buffer[241] = '\n';
|
||||
fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, (const uint8_t*)buffer, 242);
|
||||
screenbuffer_helper_add(chp, buffer, wp, '\r');
|
||||
screenbuffer_helper_add(chp, buffer, wp, '\n');
|
||||
}
|
||||
if (wp > 0) {
|
||||
// send remaining
|
||||
fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, (const uint8_t*)buffer, wp);
|
||||
}
|
||||
|
||||
evtd->exit_shell_working_mode();
|
||||
chprintf(chp, "\r\nok\r\n");
|
||||
}
|
||||
@@ -894,8 +905,8 @@ static void cmd_cpld_read(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
}
|
||||
|
||||
static void cmd_gotgps(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
const char* usage = "usage: gotgps <lat> <lon> [altitude] [speed]\r\n";
|
||||
if (argc < 2 || argc > 4) {
|
||||
const char* usage = "usage: gotgps <lat> <lon> [altitude] [speed] [satinuse]\r\n";
|
||||
if (argc < 2 || argc > 5) {
|
||||
chprintf(chp, usage);
|
||||
return;
|
||||
}
|
||||
@@ -903,21 +914,43 @@ static void cmd_gotgps(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
float lon = atof(argv[1]);
|
||||
int32_t altitude = 0;
|
||||
int32_t speed = 0;
|
||||
uint8_t satinuse = 0;
|
||||
if (argc >= 3) altitude = strtol(argv[2], NULL, 10);
|
||||
if (argc >= 4) speed = strtol(argv[3], NULL, 10);
|
||||
GPSPosDataMessage msg{lat, lon, altitude, speed};
|
||||
if (argc >= 5) satinuse = strtol(argv[4], NULL, 10);
|
||||
GPSPosDataMessage msg{lat, lon, altitude, speed, satinuse};
|
||||
EventDispatcher::send_message(msg);
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
static void cmd_gotorientation(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
const char* usage = "usage: gotorientation <angle>\r\n";
|
||||
if (argc != 1) {
|
||||
const char* usage = "usage: gotorientation <angle> [tilt]\r\n";
|
||||
if (argc != 1 && argc != 2) {
|
||||
chprintf(chp, usage);
|
||||
return;
|
||||
}
|
||||
uint16_t angle = strtol(argv[0], NULL, 10);
|
||||
OrientationDataMessage msg{angle};
|
||||
int16_t tilt = 400;
|
||||
if (argc >= 2) tilt = strtol(argv[1], NULL, 10);
|
||||
OrientationDataMessage msg{angle, tilt};
|
||||
EventDispatcher::send_message(msg);
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
static void cmd_gotenv(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
const char* usage = "usage: gotenv <temperature> [humidity] [pressure] [light]\r\n";
|
||||
if (argc < 1 || argc > 4) {
|
||||
chprintf(chp, usage);
|
||||
return;
|
||||
}
|
||||
float temp = atof(argv[0]);
|
||||
float humi = 0;
|
||||
float pressure = 0;
|
||||
uint16_t light = 0;
|
||||
if (argc > 1) humi = atof(argv[1]);
|
||||
if (argc > 2) pressure = atof(argv[2]);
|
||||
if (argc > 3) light = strtol(argv[3], NULL, 10);
|
||||
EnvironmentDataMessage msg{temp, humi, pressure, light};
|
||||
EventDispatcher::send_message(msg);
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
@@ -1044,6 +1077,7 @@ static const ShellCommand commands[] = {
|
||||
{"appstart", cmd_appstart},
|
||||
{"gotgps", cmd_gotgps},
|
||||
{"gotorientation", cmd_gotorientation},
|
||||
{"gotenv", cmd_gotenv},
|
||||
{"sysinfo", cmd_sysinfo},
|
||||
{"radioinfo", cmd_radioinfo},
|
||||
{"pmemreset", cmd_pmemreset},
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "string_format.hpp"
|
||||
#include <cstring>
|
||||
|
||||
#include "crc.hpp"
|
||||
|
||||
static File* shell_file = nullptr;
|
||||
|
||||
static bool report_on_error(BaseSequentialStream* chp, File::Error& error) {
|
||||
@@ -97,7 +99,7 @@ void cmd_sd_mkdir(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
|
||||
auto path = path_from_string8(argv[0]);
|
||||
|
||||
if (!std::filesystem::is_directory(path)) {
|
||||
if (std::filesystem::is_directory(path)) {
|
||||
chprintf(chp, "directory already exists.\r\n");
|
||||
return;
|
||||
}
|
||||
@@ -371,3 +373,34 @@ void cmd_sd_write_binary(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
|
||||
chprintf(chp, "ok\r\n");
|
||||
}
|
||||
|
||||
void cmd_sd_crc32(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
if (argc != 1) {
|
||||
chprintf(chp, "usage: crc32 <path>\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
auto path = path_from_string8(argv[0]);
|
||||
File* crc_file = new File();
|
||||
auto error = crc_file->open(path, true, false);
|
||||
if (report_on_error(chp, error)) return;
|
||||
|
||||
uint8_t buffer[64];
|
||||
CRC<32> crc{0x04c11db7, 0xffffffff, 0xffffffff};
|
||||
|
||||
while (true) {
|
||||
auto bytes_read = crc_file->read(buffer, 64);
|
||||
if (report_on_error(chp, bytes_read)) return;
|
||||
|
||||
if (bytes_read.value() > 0) {
|
||||
crc.process_bytes((void*)buffer, bytes_read.value());
|
||||
}
|
||||
|
||||
if (64 != bytes_read.value()) {
|
||||
chprintf(chp, "CRC32: 0x%08X\r\n", crc.checksum());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
delete crc_file;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ void cmd_sd_read(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_read_binary(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_write(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_write_binary(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
void cmd_sd_crc32(BaseSequentialStream* chp, int argc, char* argv[]);
|
||||
|
||||
static std::filesystem::path path_from_string8(char* path) {
|
||||
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> conv;
|
||||
@@ -62,6 +63,7 @@ static std::filesystem::path path_from_string8(char* path) {
|
||||
{"ftell", cmd_sd_tell}, \
|
||||
{"fread", cmd_sd_read}, \
|
||||
{"frb", cmd_sd_read_binary}, \
|
||||
{"fwrite", cmd_sd_write}, \
|
||||
{"fwb", cmd_sd_write_binary}
|
||||
{"fwrite", cmd_sd_write}, \
|
||||
{"fwb", cmd_sd_write_binary}, \
|
||||
{"crc32", cmd_sd_crc32}
|
||||
// clang-format on
|
||||
|
||||
@@ -290,7 +290,7 @@ macro(DeclareTargets chunk_tag name)
|
||||
DEPENDS ${PROJECT_NAME}.elf ${MAKE_IMAGE_CHUNK}
|
||||
VERBATIM
|
||||
)
|
||||
|
||||
|
||||
set(BASEBAND_IMAGES ${BASEBAND_IMAGES} ${PROJECT_NAME}.img)
|
||||
|
||||
else()
|
||||
@@ -434,12 +434,12 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PNFM nfm_audio)
|
||||
|
||||
### No op
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_noop.cpp
|
||||
)
|
||||
DeclareTargets(PNOP no_operation)
|
||||
#### No op
|
||||
#
|
||||
#set(MODE_CPPSRC
|
||||
# proc_noop.cpp
|
||||
#)
|
||||
#DeclareTargets(PNOP no_operation)
|
||||
|
||||
### OOK
|
||||
|
||||
@@ -519,7 +519,7 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PWFM wfm_audio)
|
||||
|
||||
### SubGhz Decoders
|
||||
### SubGhz Decoders
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_subghzd.cpp
|
||||
@@ -554,7 +554,7 @@ set(MODE_INCDIR
|
||||
)
|
||||
set(MODE_CPPSRC
|
||||
sd_over_usb/proc_sd_over_usb.cpp
|
||||
|
||||
|
||||
sd_over_usb/scsi.c
|
||||
sd_over_usb/diskio.c
|
||||
sd_over_usb/sd_over_usb.c
|
||||
@@ -672,7 +672,7 @@ set(BASEBAND_IMAGES ${BASEBAND_IMAGES} terminator.img)
|
||||
#######################################################################
|
||||
|
||||
project(baseband)
|
||||
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_NAME}.img
|
||||
COMMAND cat ${BASEBAND_IMAGES} > ${PROJECT_NAME}.img
|
||||
|
||||
@@ -162,14 +162,6 @@ static void rx_error() {
|
||||
disable();
|
||||
}
|
||||
|
||||
void init() {
|
||||
gpdma_channel_i2s0_tx.set_handlers(tx_transfer_complete, tx_error);
|
||||
gpdma_channel_i2s0_rx.set_handlers(rx_transfer_complete, rx_error);
|
||||
|
||||
// LPC_GPDMA->SYNC |= (1 << gpdma_rx_peripheral);
|
||||
// LPC_GPDMA->SYNC |= (1 << gpdma_tx_peripheral);
|
||||
}
|
||||
|
||||
static void configure_tx() {
|
||||
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->TXFIFO);
|
||||
const auto control_value = control_tx(transfer_bytes);
|
||||
@@ -194,22 +186,32 @@ static void configure_rx() {
|
||||
}
|
||||
}
|
||||
|
||||
void configure() {
|
||||
configure_tx();
|
||||
configure_rx();
|
||||
static void enable_tx() {
|
||||
const auto gpdma_config_tx = config_tx();
|
||||
gpdma_channel_i2s0_tx.configure(lli_tx_loop[0], gpdma_config_tx);
|
||||
gpdma_channel_i2s0_tx.enable();
|
||||
}
|
||||
|
||||
void enable() {
|
||||
const auto gpdma_config_tx = config_tx();
|
||||
static void enable_rx() {
|
||||
const auto gpdma_config_rx = config_rx();
|
||||
|
||||
gpdma_channel_i2s0_tx.configure(lli_tx_loop[0], gpdma_config_tx);
|
||||
gpdma_channel_i2s0_rx.configure(lli_rx_loop[0], gpdma_config_rx);
|
||||
|
||||
gpdma_channel_i2s0_tx.enable();
|
||||
gpdma_channel_i2s0_rx.enable();
|
||||
}
|
||||
|
||||
void init_audio_out() {
|
||||
gpdma_channel_i2s0_tx.set_handlers(tx_transfer_complete, tx_error);
|
||||
// LPC_GPDMA->SYNC |= (1 << gpdma_tx_peripheral);
|
||||
configure_tx();
|
||||
enable_tx();
|
||||
}
|
||||
|
||||
void init_audio_in() {
|
||||
gpdma_channel_i2s0_rx.set_handlers(rx_transfer_complete, rx_error);
|
||||
// LPC_GPDMA->SYNC |= (1 << gpdma_rx_peripheral);
|
||||
configure_rx();
|
||||
enable_rx();
|
||||
}
|
||||
|
||||
void disable() {
|
||||
gpdma_channel_i2s0_tx.disable();
|
||||
gpdma_channel_i2s0_rx.disable();
|
||||
@@ -217,6 +219,10 @@ void disable() {
|
||||
|
||||
void shrink_tx_buffer(bool shrink) {
|
||||
single_tx_buffer = shrink;
|
||||
|
||||
if (transfers_per_buffer == 1)
|
||||
return;
|
||||
|
||||
if (single_tx_buffer)
|
||||
lli_tx_loop[0].lli = lli_pointer(&lli_tx_loop[0]);
|
||||
else
|
||||
|
||||
@@ -43,9 +43,8 @@ using buffer_t = buffer_t<sample_t>;
|
||||
|
||||
namespace dma {
|
||||
|
||||
void init();
|
||||
void configure();
|
||||
void enable();
|
||||
void init_audio_in();
|
||||
void init_audio_out();
|
||||
void disable();
|
||||
void shrink_tx_buffer(bool shrink);
|
||||
|
||||
|
||||
@@ -28,13 +28,8 @@
|
||||
|
||||
#include "gpdma.hpp"
|
||||
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
static void init() {
|
||||
audio::dma::init();
|
||||
audio::dma::configure();
|
||||
audio::dma::enable();
|
||||
|
||||
// Audio DMA initialization was moved to baseband proc's that actually use DMA audio, to save memory.
|
||||
nvicEnableVector(DMA_IRQn, CORTEX_PRIORITY_MASK(LPC_DMA_IRQ_PRIORITY));
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "proc_afskrx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
void AFSKRxProcessor::execute(const buffer_c8_t& buffer) {
|
||||
@@ -181,6 +183,8 @@ void AFSKRxProcessor::configure(const AFSKRxConfigureMessage& message) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<AFSKRxProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "proc_am_audio.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
@@ -112,6 +113,8 @@ void NarrowbandAMAudio::capture_config(const CaptureConfigMessage& message) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<NarrowbandAMAudio>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "proc_aprsrx.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "stdio.h"
|
||||
@@ -244,6 +246,8 @@ void APRSRxProcessor::configure(const APRSRxConfigureMessage& message) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<APRSRxProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -139,6 +140,8 @@ void AudioTXProcessor::sample_rate_config(const SampleRateConfigMessage& message
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<AudioTXProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "tonesets.hpp"
|
||||
#include "event_m4.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -167,6 +168,8 @@ void MicTXProcessor::on_message(const Message* const msg) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_in();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<MicTXProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
@@ -174,6 +176,8 @@ void NarrowbandFMAudio::capture_config(const CaptureConfigMessage& message) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<NarrowbandFMAudio>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include "dsp_iir_config.hpp"
|
||||
#include "event_m4.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -530,6 +531,8 @@ uint32_t POCSAGProcessor::getRate() const {
|
||||
//
|
||||
// ====================================================================
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<POCSAGProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "proc_pocsag2.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -416,6 +417,8 @@ void POCSAGProcessor::send_packet() {
|
||||
/* main **************************************************/
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<POCSAGProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
SondeProcessor::SondeProcessor() {
|
||||
decim_0.configure(taps_11k0_decim_0.taps);
|
||||
@@ -141,6 +142,8 @@ void SondeProcessor::pitch_rssi_config(const PitchRSSIConfigureMessage& message)
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<SondeProcessor>()};
|
||||
event_dispatcher.run();
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "proc_tones.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "event_m4.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -154,6 +155,8 @@ void TonesProcessor::on_message(const Message* const p) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<TonesProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "audio_output.hpp"
|
||||
#include "dsp_fft.hpp"
|
||||
#include "event_m4.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -188,6 +189,8 @@ void WidebandFMAudio::capture_config(const CaptureConfigMessage& message) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<WidebandFMAudio>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
*/
|
||||
|
||||
#include "proc_wideband_spectrum.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
#include "event_m4.hpp"
|
||||
|
||||
@@ -82,6 +83,8 @@ void WidebandSpectrum::on_message(const Message* const msg) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out(); // for AudioRX app (enables audio output while this baseband image is running)
|
||||
|
||||
EventDispatcher event_dispatcher{std::make_unique<WidebandSpectrum>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
|
||||
@@ -22,12 +22,12 @@
|
||||
#ifndef __RSSI_STATS_COLLECTOR_H__
|
||||
#define __RSSI_STATS_COLLECTOR_H__
|
||||
|
||||
#include "rssi.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
#include "rssi.hpp"
|
||||
#include "message.hpp"
|
||||
|
||||
class RSSIStatisticsCollector {
|
||||
public:
|
||||
template <typename Callback>
|
||||
|
||||
@@ -120,6 +120,7 @@ class Message {
|
||||
SubGhzDData = 62,
|
||||
GPSPosData = 63,
|
||||
OrientationData = 64,
|
||||
EnvironmentData = 65,
|
||||
MAX
|
||||
};
|
||||
|
||||
@@ -132,10 +133,10 @@ class Message {
|
||||
};
|
||||
|
||||
struct RSSIStatistics {
|
||||
uint32_t accumulator{0};
|
||||
uint32_t min{0};
|
||||
uint32_t max{0};
|
||||
uint32_t count{0};
|
||||
uint16_t accumulator{0};
|
||||
uint8_t min{0};
|
||||
uint8_t max{0};
|
||||
uint16_t count{0};
|
||||
};
|
||||
|
||||
class RSSIStatisticsMessage : public Message {
|
||||
@@ -1312,27 +1313,52 @@ class GPSPosDataMessage : public Message {
|
||||
float lat = 200.0,
|
||||
float lon = 200.0,
|
||||
int32_t altitude = 0,
|
||||
int32_t speed = 0)
|
||||
int32_t speed = 0,
|
||||
uint8_t satinuse = 0)
|
||||
: Message{ID::GPSPosData},
|
||||
lat{lat},
|
||||
lon{lon},
|
||||
altitude{altitude},
|
||||
speed{speed} {
|
||||
speed{speed},
|
||||
satinuse{satinuse} {
|
||||
}
|
||||
float lat = 200.0;
|
||||
float lon = 200.0;
|
||||
int32_t altitude = 0;
|
||||
int32_t speed = 0;
|
||||
uint8_t satinuse = 0;
|
||||
};
|
||||
|
||||
class OrientationDataMessage : public Message {
|
||||
public:
|
||||
constexpr OrientationDataMessage(
|
||||
uint16_t angle = 400)
|
||||
uint16_t angle = 400,
|
||||
int16_t tilt = 400)
|
||||
: Message{ID::OrientationData},
|
||||
angle{angle} {
|
||||
angle{angle},
|
||||
tilt{tilt} {
|
||||
}
|
||||
uint16_t angle = 400; //>360 -> no orientation set
|
||||
int16_t tilt = 400;
|
||||
};
|
||||
|
||||
class EnvironmentDataMessage : public Message {
|
||||
public:
|
||||
constexpr EnvironmentDataMessage(
|
||||
float temperature = 0,
|
||||
float humidity = 0,
|
||||
float pressure = 0,
|
||||
uint16_t light = 0)
|
||||
: Message{ID::EnvironmentData},
|
||||
temperature{temperature},
|
||||
humidity{humidity},
|
||||
pressure{pressure},
|
||||
light{light} {
|
||||
}
|
||||
float temperature = 0; // celsius
|
||||
float humidity = 0; // percent (rh)
|
||||
float pressure = 0; // hpa
|
||||
uint16_t light = 0; // lux
|
||||
};
|
||||
|
||||
#endif /*__MESSAGE_H__*/
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyleft (ɔ) 2024 zxkmm under GPL license
|
||||
* Copyright (C) 2024 u-foka
|
||||
* Copyright (C) 2024 Mark Thompson
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
@@ -34,6 +35,21 @@
|
||||
|
||||
// #include "portapack_persistent_memory.hpp"
|
||||
|
||||
// Darkened pixel bit mask for each possible shift value.
|
||||
static const uint16_t darken_mask[4] = {
|
||||
0b1111111111111111, // RrrrrGgggggBbbbb
|
||||
0b0111101111101111, // 0Rrrr0Ggggg0Bbbb
|
||||
0b0011100111100111, // 00Rrr00Gggg00Bbb
|
||||
0b0001100011100011 // 000Rr000Ggg000Bb
|
||||
};
|
||||
|
||||
// To darken, dividing each color level R/G/B by 2^shift.
|
||||
#define DARKENED_PIXEL(pixel, shift) ((pixel >> shift) & darken_mask[shift])
|
||||
|
||||
// To un-darken, multiply each color level by 2^shift (might still be darker that before since some bits may have been lost above).
|
||||
// This function will only be called when the pixel has previously been darkened, so no masking is needed.
|
||||
#define UNDARKENED_PIXEL(pixel, shift) (pixel << shift)
|
||||
|
||||
namespace portapack {
|
||||
|
||||
class IO {
|
||||
@@ -147,7 +163,7 @@ class IO {
|
||||
|
||||
void lcd_write_pixel(ui::Color pixel) {
|
||||
if (get_dark_cover()) {
|
||||
darken_color(pixel, get_brightness()); // Darken the pixel color
|
||||
pixel.v = DARKENED_PIXEL(pixel.v, get_brightness());
|
||||
}
|
||||
lcd_write_data(pixel.v);
|
||||
}
|
||||
@@ -158,7 +174,7 @@ class IO {
|
||||
|
||||
void lcd_write_pixels(ui::Color pixel, size_t n) {
|
||||
if (get_dark_cover()) {
|
||||
darken_color(pixel, get_brightness()); // Darken the pixel color
|
||||
pixel.v = DARKENED_PIXEL(pixel.v, get_brightness());
|
||||
}
|
||||
while (n--) {
|
||||
lcd_write_data(pixel.v);
|
||||
@@ -167,7 +183,7 @@ class IO {
|
||||
|
||||
void lcd_write_pixels_unrolled8(ui::Color pixel, size_t n) {
|
||||
if (get_dark_cover()) {
|
||||
darken_color(pixel, get_brightness()); // Darken the pixel color
|
||||
pixel.v = DARKENED_PIXEL(pixel.v, get_brightness());
|
||||
}
|
||||
auto v = pixel.v;
|
||||
n >>= 3;
|
||||
@@ -331,25 +347,6 @@ class IO {
|
||||
addr(1); /* Set up for data phase (most likely after a command) */
|
||||
}
|
||||
|
||||
void darken_color(ui::Color& pixel, uint8_t darken_level_shift) {
|
||||
// TODO: 1. do we need edge control?
|
||||
// currently didn't see and issue without edge control
|
||||
// but maybe hurts screen hardware without one?
|
||||
|
||||
// TODO: 2. de-color mode for accessibility
|
||||
// TODO: 3. high contrast mode for accessibility
|
||||
|
||||
uint16_t r = (pixel.v >> 11) & 0x1F; // Extract red
|
||||
uint16_t g = (pixel.v >> 5) & 0x3F; // Extract green
|
||||
uint16_t b = pixel.v & 0x1F; // Extract blue
|
||||
|
||||
r = r >> darken_level_shift; // Darken red
|
||||
g = g >> darken_level_shift; // Darken green
|
||||
b = b >> darken_level_shift; // Darken blue
|
||||
|
||||
pixel.v = (r << 11) | (g << 5) | b; // Combine back to color, check UI::color for the color layout
|
||||
}
|
||||
|
||||
// void high_contrast(ui::Color& pixel, size_t contrast_level_shift) { // TODO
|
||||
// uint16_t r = (pixel.v >> 11) & 0x1F;
|
||||
// uint16_t g = (pixel.v >> 5) & 0x3F;
|
||||
@@ -417,7 +414,12 @@ class IO {
|
||||
halPolledDelay(18); // 90ns
|
||||
|
||||
const auto value_low = data_read();
|
||||
return (value_high << 8) | value_low;
|
||||
uint32_t original_value = (value_high << 8) | value_low;
|
||||
|
||||
if (get_dark_cover()) {
|
||||
original_value = UNDARKENED_PIXEL(original_value, get_brightness());
|
||||
}
|
||||
return original_value;
|
||||
}
|
||||
|
||||
void io_write(const bool address, const uint_fast16_t value) {
|
||||
|
||||
@@ -435,6 +435,7 @@ void defaults() {
|
||||
set_recon_load_hamradios(true);
|
||||
set_recon_match_mode(0);
|
||||
set_recon_repeat_recorded(false);
|
||||
set_recon_repeat_recorded_file_mode(false); // false delete repeater , true keep repeated
|
||||
set_recon_repeat_amp(false);
|
||||
set_recon_repeat_gain(35);
|
||||
set_recon_repeat_nb(3);
|
||||
@@ -784,38 +785,63 @@ void set_clkout_freq(uint16_t freq) {
|
||||
}
|
||||
|
||||
/* Recon app */
|
||||
enum recon_config_bits {
|
||||
RC_UNUSED_BIT = 63, // just a reminder that this is a 64-bit field
|
||||
RC_AUTOSAVE_FREQS = 31,
|
||||
RC_AUTOSTART_RECON = 30,
|
||||
RC_CONTINUOUS = 29,
|
||||
RC_CLEAR_OUTPUT = 28,
|
||||
RC_LOAD_FREQS = 27,
|
||||
RC_LOAD_RANGES = 26,
|
||||
RC_UPDATE_RANGES = 25,
|
||||
RC_LOAD_HAMRADIOS = 24,
|
||||
RC_MATCH_MODE = 23,
|
||||
RC_AUTO_RECORD_LOCKED = 22,
|
||||
RC_REPEAT_RECORDED = 21,
|
||||
RC_REPEAT_AMP = 20,
|
||||
RC_LOAD_REPEATERS = 19,
|
||||
RC_REPEAT_FILE_MODE = 18,
|
||||
};
|
||||
|
||||
bool check_recon_config_bit(uint8_t rc_bit) {
|
||||
return ((data->recon_config >> rc_bit) & 1) != 0;
|
||||
}
|
||||
void set_recon_config_bit(uint8_t rc_bit, bool v) {
|
||||
auto bit_mask = 1LL << rc_bit;
|
||||
data->recon_config = v ? (data->recon_config | bit_mask) : (data->recon_config & ~bit_mask);
|
||||
}
|
||||
bool recon_autosave_freqs() {
|
||||
return (data->recon_config & 0x80000000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_AUTOSAVE_FREQS);
|
||||
}
|
||||
bool recon_autostart_recon() {
|
||||
return (data->recon_config & 0x40000000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_AUTOSTART_RECON);
|
||||
}
|
||||
bool recon_continuous() {
|
||||
return (data->recon_config & 0x20000000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_CONTINUOUS);
|
||||
}
|
||||
bool recon_clear_output() {
|
||||
return (data->recon_config & 0x10000000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_CLEAR_OUTPUT);
|
||||
}
|
||||
bool recon_load_freqs() {
|
||||
return (data->recon_config & 0x08000000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_LOAD_FREQS);
|
||||
}
|
||||
bool recon_load_ranges() {
|
||||
return (data->recon_config & 0x04000000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_LOAD_RANGES);
|
||||
}
|
||||
bool recon_update_ranges_when_recon() {
|
||||
return (data->recon_config & 0x02000000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_UPDATE_RANGES);
|
||||
}
|
||||
bool recon_load_hamradios() {
|
||||
return (data->recon_config & 0x01000000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_LOAD_HAMRADIOS);
|
||||
}
|
||||
bool recon_match_mode() {
|
||||
return (data->recon_config & 0x00800000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_MATCH_MODE);
|
||||
}
|
||||
bool recon_auto_record_locked() {
|
||||
return (data->recon_config & 0x00400000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_AUTO_RECORD_LOCKED);
|
||||
}
|
||||
bool recon_repeat_recorded() {
|
||||
return (data->recon_config & 0x00200000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_REPEAT_RECORDED);
|
||||
}
|
||||
int8_t recon_repeat_nb() {
|
||||
return data->recon_repeat_nb;
|
||||
@@ -827,44 +853,46 @@ uint8_t recon_repeat_delay() {
|
||||
return data->recon_repeat_delay;
|
||||
}
|
||||
bool recon_repeat_amp() {
|
||||
return (data->recon_config & 0x00100000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_REPEAT_AMP);
|
||||
}
|
||||
bool recon_load_repeaters() {
|
||||
return (data->recon_config & 0x00080000UL) ? true : false;
|
||||
return check_recon_config_bit(RC_LOAD_REPEATERS);
|
||||
}
|
||||
bool recon_repeat_recorded_file_mode() {
|
||||
return check_recon_config_bit(RC_REPEAT_FILE_MODE);
|
||||
}
|
||||
|
||||
void set_recon_autosave_freqs(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x80000000UL) | (v << 31);
|
||||
set_recon_config_bit(RC_AUTOSAVE_FREQS, v);
|
||||
}
|
||||
void set_recon_autostart_recon(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x40000000UL) | (v << 30);
|
||||
set_recon_config_bit(RC_AUTOSTART_RECON, v);
|
||||
}
|
||||
void set_recon_continuous(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x20000000UL) | (v << 29);
|
||||
set_recon_config_bit(RC_CONTINUOUS, v);
|
||||
}
|
||||
void set_recon_clear_output(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x10000000UL) | (v << 28);
|
||||
set_recon_config_bit(RC_CLEAR_OUTPUT, v);
|
||||
}
|
||||
void set_recon_load_freqs(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x08000000UL) | (v << 27);
|
||||
set_recon_config_bit(RC_LOAD_FREQS, v);
|
||||
}
|
||||
void set_recon_load_ranges(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x04000000UL) | (v << 26);
|
||||
set_recon_config_bit(RC_LOAD_RANGES, v);
|
||||
}
|
||||
void set_recon_update_ranges_when_recon(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x02000000UL) | (v << 25);
|
||||
set_recon_config_bit(RC_UPDATE_RANGES, v);
|
||||
}
|
||||
void set_recon_load_hamradios(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x01000000UL) | (v << 24);
|
||||
set_recon_config_bit(RC_LOAD_HAMRADIOS, v);
|
||||
}
|
||||
void set_recon_match_mode(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x00800000UL) | (v << 23);
|
||||
set_recon_config_bit(RC_MATCH_MODE, v);
|
||||
}
|
||||
void set_recon_auto_record_locked(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x00400000UL) | (v << 22);
|
||||
set_recon_config_bit(RC_AUTO_RECORD_LOCKED, v);
|
||||
}
|
||||
void set_recon_repeat_recorded(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x00200000UL) | (v << 21);
|
||||
set_recon_config_bit(RC_REPEAT_RECORDED, v);
|
||||
}
|
||||
void set_recon_repeat_nb(const int8_t v) {
|
||||
data->recon_repeat_nb = v;
|
||||
@@ -876,10 +904,13 @@ void set_recon_repeat_delay(const uint8_t v) {
|
||||
data->recon_repeat_delay = v;
|
||||
}
|
||||
void set_recon_repeat_amp(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x00100000UL) | (v << 20);
|
||||
set_recon_config_bit(RC_REPEAT_AMP, v);
|
||||
}
|
||||
void set_recon_load_repeaters(const bool v) {
|
||||
data->recon_config = (data->recon_config & ~0x00080000UL) | (v << 19);
|
||||
set_recon_config_bit(RC_LOAD_REPEATERS, v);
|
||||
}
|
||||
void set_recon_repeat_recorded_file_mode(const bool v) {
|
||||
set_recon_config_bit(RC_REPEAT_FILE_MODE, v);
|
||||
}
|
||||
|
||||
/* UI Config 2 */
|
||||
|
||||
@@ -298,6 +298,7 @@ bool recon_load_ranges();
|
||||
bool recon_update_ranges_when_recon();
|
||||
bool recon_auto_record_locked();
|
||||
bool recon_repeat_recorded();
|
||||
bool recon_repeat_recorded_file_mode();
|
||||
int8_t recon_repeat_nb();
|
||||
int8_t recon_repeat_gain();
|
||||
bool recon_repeat_amp();
|
||||
@@ -313,6 +314,7 @@ void set_recon_load_ranges(const bool v);
|
||||
void set_recon_update_ranges_when_recon(const bool v);
|
||||
void set_recon_auto_record_locked(const bool v);
|
||||
void set_recon_repeat_recorded(const bool v);
|
||||
void set_recon_repeat_recorded_file_mode(const bool v);
|
||||
void set_recon_repeat_nb(const int8_t v);
|
||||
void set_recon_repeat_gain(const int8_t v);
|
||||
void set_recon_repeat_amp(const bool v);
|
||||
|
||||
@@ -73,6 +73,7 @@ struct image_tag_t {
|
||||
char c[4];
|
||||
};
|
||||
|
||||
constexpr image_tag_t image_tag_none{0, 0, 0, 0};
|
||||
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_afsk_rx{'P', 'A', 'F', 'R'};
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 292 B After Width: | Height: | Size: 4.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 182 B |
@@ -0,0 +1,82 @@
|
||||
# Drone Analog FPV Channels
|
||||
# https://oscarliang.com/fpv-channels/
|
||||
f=5865000000,d=FPV Ch A-1
|
||||
f=5845000000,d=FPV Ch A-2
|
||||
f=5825000000,d=FPV Ch A-3
|
||||
f=5805000000,d=FPV Ch A-4
|
||||
f=5785000000,d=FPV Ch A-5
|
||||
f=5765000000,d=FPV Ch A-6
|
||||
f=5745000000,d=FPV Ch A-7
|
||||
f=5725000000,d=FPV Ch A-8
|
||||
f=5733000000,d=FPV Ch B-1
|
||||
f=5752000000,d=FPV Ch B-2
|
||||
f=5771000000,d=FPV Ch B-3
|
||||
f=5790000000,d=FPV Ch B-4
|
||||
f=5809000000,d=FPV Ch B-5
|
||||
f=5828000000,d=FPV Ch B-6
|
||||
f=5847000000,d=FPV Ch B-7
|
||||
f=5866000000,d=FPV Ch B-8
|
||||
f=5705000000,d=FPV Ch E-1
|
||||
f=5685000000,d=FPV Ch E-2
|
||||
f=5665000000,d=FPV Ch E-3
|
||||
f=5645000000,d=FPV Ch E-4
|
||||
f=5885000000,d=FPV Ch E-5
|
||||
f=5905000000,d=FPV Ch E-6
|
||||
f=5925000000,d=FPV Ch E-7
|
||||
f=5945000000,d=FPV Ch E-8
|
||||
f=5740000000,d=FPV Ch F-1
|
||||
f=5760000000,d=FPV Ch F-2
|
||||
f=5780000000,d=FPV Ch F-3
|
||||
f=5800000000,d=FPV Ch F-4
|
||||
f=5820000000,d=FPV Ch F-5
|
||||
f=5840000000,d=FPV Ch F-6
|
||||
f=5860000000,d=FPV Ch F-7
|
||||
f=5880000000,d=FPV Ch F-8
|
||||
f=5658000000,d=FPV Ch R-1
|
||||
f=5695000000,d=FPV Ch R-2
|
||||
f=5732000000,d=FPV Ch R-3
|
||||
f=5769000000,d=FPV Ch R-4
|
||||
f=5806000000,d=FPV Ch R-5
|
||||
f=5843000000,d=FPV Ch R-6
|
||||
f=5880000000,d=FPV Ch R-7
|
||||
f=5917000000,d=FPV Ch R-8
|
||||
f=5362000000,d=FPV Ch D-1
|
||||
f=5399000000,d=FPV Ch D-2
|
||||
f=5436000000,d=FPV Ch D-3
|
||||
f=5473000000,d=FPV Ch D-4
|
||||
f=5510000000,d=FPV Ch D-5
|
||||
f=5547000000,d=FPV Ch D-6
|
||||
f=5584000000,d=FPV Ch D-7
|
||||
f=5621000000,d=FPV Ch D-8
|
||||
f=5325000000,d=FPV Ch U-1
|
||||
f=5348000000,d=FPV Ch U-2
|
||||
f=5366000000,d=FPV Ch U-3
|
||||
f=5384000000,d=FPV Ch U-4
|
||||
f=5402000000,d=FPV Ch U-5
|
||||
f=5420000000,d=FPV Ch U-6
|
||||
f=5438000000,d=FPV Ch U-7
|
||||
f=5456000000,d=FPV Ch U-8
|
||||
f=5474000000,d=FPV Ch O-1
|
||||
f=5492000000,d=FPV Ch O-2
|
||||
f=5510000000,d=FPV Ch O-3
|
||||
f=5528000000,d=FPV Ch O-4
|
||||
f=5546000000,d=FPV Ch O-5
|
||||
f=5564000000,d=FPV Ch O-6
|
||||
f=5582000000,d=FPV Ch O-7
|
||||
f=5600000000,d=FPV Ch O-8
|
||||
f=5333000000,d=FPV Ch L-1
|
||||
f=5373000000,d=FPV Ch L-2
|
||||
f=5413000000,d=FPV Ch L-3
|
||||
f=5453000000,d=FPV Ch L-4
|
||||
f=5493000000,d=FPV Ch L-5
|
||||
f=5533000000,d=FPV Ch L-6
|
||||
f=5573000000,d=FPV Ch L-7
|
||||
f=5613000000,d=FPV Ch L-8
|
||||
f=5653000000,d=FPV Ch H-1
|
||||
f=5693000000,d=FPV Ch H-2
|
||||
f=5733000000,d=FPV Ch H-3
|
||||
f=5773000000,d=FPV Ch H-4
|
||||
f=5813000000,d=FPV Ch H-5
|
||||
f=5853000000,d=FPV Ch H-6
|
||||
f=5893000000,d=FPV Ch H-7
|
||||
f=5933000000,d=FPV Ch H-8
|
||||
+22
-22
@@ -1,22 +1,22 @@
|
||||
a=150000,b=285000,m=AM,bw=DSB,s=5kHz,d=Longwave broadcast band|BCB (EU)
|
||||
a=525000,b=1605000,m=AM,bw=DSB,s=5kHz,d=AM broadcast band|BCB (EU & J)
|
||||
a=530000,b=1710000,m=AM,bw=DSB,s=5kHz,d=AM broadcast band|BCB (US)
|
||||
a=1800000,b=29700000,m=WFM,bw=16k,d=Amateur_radio|Amateur
|
||||
a=26900000,b=27400000,m=WFM,bw=16k,d=Citizens_band_radio|Citizens band
|
||||
a=28000000,b=30000000,m=WFM,bw=16k,d=Amateur_radio|Amateur
|
||||
a=29000000,b=54000000,m=WFM,bw=16k,d=Land mobile
|
||||
a=50000000,b=54000000,m=WFM,bw=16k,d=Amateur_radio|Amateur
|
||||
a=65000000,b=85000000,m=WFM,bw=16k,d=Land mobile (EU)
|
||||
a=108000000,b=136000000,m=WFM,bw=16k,d=Aircraft
|
||||
a=120000000,b=160000000,m=WFM,bw=16k,s=50kHz,d=Land mobile (EU)
|
||||
a=132000000,b=174000000,m=WFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=142000000,b=170000000,m=WFM,bw=16k,s=50kHz,d=Land mobile (J)
|
||||
a=144000000,b=148000000,m=WFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
|
||||
a=216000000,b=222000000,m=WFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=222000000,b=225000000,m=WFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
|
||||
a=335000000,b=384000000,m=WFM,bw=16k,s=50kHz,d=Land mobile (J)
|
||||
a=406000000,b=512000000,m=WFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=450000000,b=470000000,m=WFM,bw=16k,s=50kHz,d=Land mobile (J)
|
||||
a=430000000,b=450000000,m=WFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
|
||||
a=806000000,b=947000000,m=WFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=1200000000,b=1600000000,m=WFM,bw=16k,s=50kHz,d=Amateur|Land mobile|GPS
|
||||
a=150000,b=285000,m=AM,bw=DSB 9k,s=5kHz,d=Longwave broadcast band|BCB (EU)
|
||||
a=525000,b=1605000,m=AM,bw=DSB 9k,s=5kHz,d=AM broadcast band|BCB (EU & J)
|
||||
a=530000,b=1710000,m=AM,bw=DSB 9k,s=5kHz,d=AM broadcast band|BCB (US)
|
||||
a=1800000,b=29700000,m=AM,bw=LSB-3k,d=Amateur_radio|Amateur
|
||||
a=26900000,b=27400000,m=AM,bw=DSB 9k,d=Citizens_band_radio|Citizens band
|
||||
a=28000000,b=30000000,m=AM,bw=USB+3k,d=Amateur_radio|Amateur
|
||||
a=29000000,b=54000000,m=AM,bw=DSB 9k,d=Land mobile
|
||||
a=50000000,b=54000000,m=NFM,bw=16k,d=Amateur_radio|Amateur
|
||||
a=65000000,b=85000000,m=NFM,bw=16k,d=Land mobile (EU)
|
||||
a=108000000,b=136000000,m=NFM,bw=16k,d=Aircraft
|
||||
a=120000000,b=160000000,m=NFM,bw=16k,s=50kHz,d=Land mobile (EU)
|
||||
a=132000000,b=174000000,m=NFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=142000000,b=170000000,m=NFM,bw=16k,s=50kHz,d=Land mobile (J)
|
||||
a=144000000,b=148000000,m=NFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
|
||||
a=216000000,b=222000000,m=NFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=222000000,b=225000000,m=NFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
|
||||
a=335000000,b=384000000,m=NFM,bw=16k,s=50kHz,d=Land mobile (J)
|
||||
a=406000000,b=512000000,m=NFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=450000000,b=470000000,m=NFM,bw=16k,s=50kHz,d=Land mobile (J)
|
||||
a=430000000,b=450000000,m=NFM,bw=16k,s=50kHz,d=Amateur_radio|Amateur
|
||||
a=806000000,b=947000000,m=NFM,bw=16k,s=50kHz,d=Land mobile
|
||||
a=1200000000,b=1600000000,m=NFM,bw=16k,s=50kHz,d=Amateur|Land mobile|GPS
|
||||
|
||||
Reference in New Issue
Block a user