mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-14 02:29:29 +00:00
Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b66d8b1aa1 | |||
| 8dc3851b55 | |||
| aef7c2be93 | |||
| 4e38dc4d3e | |||
| 520ad97f78 | |||
| 5bd208bca1 | |||
| a65ef3ce2e | |||
| b2bb37af74 | |||
| 54ad82a423 | |||
| e463405c77 | |||
| c413f925a9 | |||
| 9e1eb31150 | |||
| 7eb08f34cb | |||
| 2de1f8e6bf | |||
| 55db0e8c87 | |||
| dfa35b4290 | |||
| 0124051c4f | |||
| 65f301399f | |||
| 97889494cf |
@@ -61,6 +61,7 @@ cmake-build-debug/
|
||||
*.sublime-workspace
|
||||
.vscode
|
||||
.idea
|
||||
*.swp
|
||||
|
||||
# VSCodium extensions
|
||||
.history
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
cmake_policy(SET CMP0005 NEW)
|
||||
|
||||
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/firmware/toolchain-arm-cortex-m.cmake)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
> The only legitimate link to our repositories is the [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware) organization on GitHub.--->
|
||||
# PortaPack Mayhem
|
||||
|
||||
[](https://github.com/portapack-mayhem/mayhem-firmware/actions/workflows/create_nightly_release.yml) [](https://codescene.io/projects/8381) [](https://github.com/portapack-mayhem/mayhem-firmware/releases) [](https://github.com/portapack-mayhem/mayhem-firmware/releases/latest) [](https://hub.docker.com/r/eried/portapack) [](https://discord.gg/tuwVMv3)
|
||||
[](https://github.com/portapack-mayhem/mayhem-firmware/actions/workflows/create_nightly_release.yml) [](https://codescene.io/projects/8381) [](https://github.com/portapack-mayhem/mayhem-firmware/releases) [](https://github.com/portapack-mayhem/mayhem-firmware/releases/latest) [](https://hub.docker.com/r/eried/portapack) [](https://discord.gg/tuwVMv3)
|
||||
|
||||
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). A fork is a derivate, in this case one that has extra features and fixes when compared to the older versions.
|
||||
|
||||
@@ -26,7 +26,7 @@ This repository expands upon the previous work by many people and aims to consta
|
||||
## What to buy?
|
||||
|
||||
<!---not direct to h4m but to opensourcesdrlab https://share.hackrf.app/TUOLYI--->
|
||||
:heavy_check_mark:  The fabulous H4M [complete](https://share.hackrf.app/HR60Q4) or [upgrade](https://share.hackrf.app/FPLM1H), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://share.hackrf.app/99SAMT). Join their giveaways on discord (check the badge on top). _EU customers_ can purchase via [Lab401](https://lab401.com/products/portapack-h4m).
|
||||
:heavy_check_mark:  The fabulous H4M [complete](https://share.hackrf.app/HR60Q4) or [upgrade](https://share.hackrf.app/FPLM1H), featuring numerous improvements and accessories. Sold by our friends at [OpenSourceSDRLab](https://share.hackrf.app/99SAMT). Join their giveaways on discord (check the badge on top). _EU customers_ can purchase via [Lab401](https://share.hackrf.app/0CI2CR).
|
||||
|
||||
:heavy_check_mark: A recommended one is this [PortaPack H2](https://www.ebay.com/itm/116382397447), that includes everything you need with the plastic case "inspired" on [this](https://github.com/portapack-mayhem/mayhem-firmware/wiki/3d-printed-enclosure).
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(firmware)
|
||||
|
||||
|
||||
@@ -305,7 +305,6 @@ set(CPPSRC
|
||||
apps/ui_recon.cpp
|
||||
apps/ui_scanner.cpp
|
||||
apps/ui_sd_over_usb.cpp
|
||||
apps/ui_sd_wipe.cpp
|
||||
apps/ui_search.cpp
|
||||
apps/ui_settings.cpp
|
||||
apps/ui_siggen.cpp
|
||||
@@ -317,9 +316,7 @@ set(CPPSRC
|
||||
apps/ui_text_editor.cpp
|
||||
apps/ui_touch_calibration.cpp
|
||||
apps/ui_touchtunes.cpp
|
||||
apps/ui_view_wav.cpp
|
||||
apps/ui_weatherstation.cpp
|
||||
apps/ui_whipcalc.cpp
|
||||
protocols/aprs.cpp
|
||||
protocols/ax25.cpp
|
||||
protocols/bht.cpp
|
||||
@@ -377,7 +374,7 @@ set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC
|
||||
hw
|
||||
apps
|
||||
protocols
|
||||
bitmaps
|
||||
bmps
|
||||
)
|
||||
|
||||
#
|
||||
|
||||
@@ -39,6 +39,14 @@ using namespace portapack;
|
||||
using namespace modems;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
#define BLE_RX_NO_ERROR 0
|
||||
#define BLE_RX_LIST_FILENAME_EMPTY_ERROR 1
|
||||
#define BLE_RX_ENTRY_FILENAME_EMPTY_ERROR 2
|
||||
#define BLE_RX_LIST_SAVE_ERROR 3
|
||||
#define BLE_RX_ENTRY_SAVE_ERROR 4
|
||||
|
||||
static uint8_t ble_rx_error = BLE_RX_NO_ERROR;
|
||||
|
||||
void BLELogger::log_raw_data(const std::string& data) {
|
||||
log_file.write_entry(data);
|
||||
}
|
||||
@@ -202,19 +210,23 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl
|
||||
}
|
||||
|
||||
void BleRecentEntryDetailView::on_save_file(const std::string value, BLETxPacket packetToSave) {
|
||||
ensure_directory(packet_save_path);
|
||||
auto folder = packet_save_path.parent_path();
|
||||
auto ext = packet_save_path.extension();
|
||||
auto new_path = folder / value + ext;
|
||||
|
||||
saveFile(new_path, packetToSave);
|
||||
if (value.length() > 0) {
|
||||
ensure_directory(packet_save_path);
|
||||
auto folder = packet_save_path.parent_path();
|
||||
auto ext = packet_save_path.extension();
|
||||
auto new_path = folder / value + ext;
|
||||
ble_rx_error = saveFile(new_path, packetToSave);
|
||||
} else {
|
||||
nav_.pop();
|
||||
ble_rx_error = BLE_RX_ENTRY_FILENAME_EMPTY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
bool BleRecentEntryDetailView::saveFile(const std::filesystem::path& path, BLETxPacket packetToSave) {
|
||||
File f;
|
||||
auto error = f.create(path);
|
||||
if (error)
|
||||
return false;
|
||||
return BLE_RX_ENTRY_SAVE_ERROR;
|
||||
|
||||
std::string macAddressStr = packetToSave.macAddress;
|
||||
std::string advertisementDataStr = packetToSave.advertisementData;
|
||||
@@ -224,7 +236,7 @@ bool BleRecentEntryDetailView::saveFile(const std::filesystem::path& path, BLETx
|
||||
|
||||
f.write(packetString.c_str(), packetString.length());
|
||||
|
||||
return true;
|
||||
return BLE_RX_NO_ERROR;
|
||||
}
|
||||
|
||||
void BleRecentEntryDetailView::update_data() {
|
||||
@@ -568,11 +580,14 @@ std::string BLERxView::build_line_str(BleRecentEntry entry) {
|
||||
}
|
||||
|
||||
void BLERxView::on_save_file(const std::string value) {
|
||||
auto folder = packet_save_path.parent_path();
|
||||
auto ext = packet_save_path.extension();
|
||||
auto new_path = folder / value + ext;
|
||||
|
||||
saveFile(new_path);
|
||||
if (value.length() > 0) {
|
||||
auto folder = packet_save_path.parent_path();
|
||||
auto ext = packet_save_path.extension();
|
||||
auto new_path = folder / value + ext;
|
||||
ble_rx_error = saveFile(new_path);
|
||||
} else {
|
||||
ble_rx_error = BLE_RX_LIST_FILENAME_EMPTY_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
bool BLERxView::saveFile(const std::filesystem::path& path) {
|
||||
@@ -584,7 +599,7 @@ bool BLERxView::saveFile(const std::filesystem::path& path) {
|
||||
auto error = src->open(path, false, true);
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
return BLE_RX_LIST_SAVE_ERROR;
|
||||
}
|
||||
|
||||
for (const auto& entry : recent) {
|
||||
@@ -615,7 +630,7 @@ bool BLERxView::saveFile(const std::filesystem::path& path) {
|
||||
auto error = dst->open(tempFilePath, false, true);
|
||||
|
||||
if (error) {
|
||||
return false;
|
||||
return BLE_RX_LIST_SAVE_ERROR;
|
||||
}
|
||||
|
||||
dst->write_line(headerStr.c_str());
|
||||
@@ -695,7 +710,7 @@ bool BLERxView::saveFile(const std::filesystem::path& path) {
|
||||
|
||||
tempList.clear();
|
||||
|
||||
return true;
|
||||
return BLE_RX_NO_ERROR;
|
||||
}
|
||||
|
||||
void BLERxView::on_data(BlePacketData* packet) {
|
||||
@@ -831,6 +846,18 @@ void BLERxView::on_timer() {
|
||||
baseband::set_btlerx(randomChannel);
|
||||
}
|
||||
}
|
||||
if (ble_rx_error != BLE_RX_NO_ERROR) {
|
||||
if (ble_rx_error == BLE_RX_LIST_FILENAME_EMPTY_ERROR) {
|
||||
nav_.display_modal("Error", "List filename is empty !");
|
||||
} else if (ble_rx_error == BLE_RX_ENTRY_FILENAME_EMPTY_ERROR) {
|
||||
nav_.display_modal("Error", "Entry filename is empty !");
|
||||
} else if (ble_rx_error == BLE_RX_LIST_SAVE_ERROR) {
|
||||
nav_.display_modal("Error", "Couldn't save list !");
|
||||
} else if (ble_rx_error == BLE_RX_ENTRY_SAVE_ERROR) {
|
||||
nav_.display_modal("Error", "Couldn't save entry !");
|
||||
}
|
||||
ble_rx_error = BLE_RX_NO_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
void BLERxView::handle_entries_sort(uint8_t index) {
|
||||
|
||||
@@ -7,30 +7,33 @@
|
||||
namespace ui {
|
||||
|
||||
// Information: a line starting with a '#' will be yellow coloured
|
||||
constexpr std::string_view authors_list[] = {
|
||||
"# * List of contributors * ",
|
||||
constexpr std::string_view mayhem_information_list[] = {
|
||||
"#****** Mayhem Community ******",
|
||||
" ",
|
||||
" https://discord.mayhem.app",
|
||||
" ",
|
||||
"#**** List of contributors ****",
|
||||
" ",
|
||||
"#Mayhem-Firmware:",
|
||||
"jboone,eried,furrtek,",
|
||||
"NotherNgineer,gullradriel,",
|
||||
"jLynx,kallanreed,Brumi-2021,",
|
||||
"htotoo,bernd-herzog,zxkmm,",
|
||||
"htotoo,zxkmm,bernd-herzog,",
|
||||
"ArjanOnwezen,euquiq,u-foka,",
|
||||
"iNetro,heurist1,dqs105,",
|
||||
"teixeluis,jwetzell,",
|
||||
"jimilinuxguy,gregoryfenton,",
|
||||
"notpike,strijar,BehleZebub,",
|
||||
"arneluehrs,rascafr,joyel24,",
|
||||
"ImDroided,zigad,johnelder,",
|
||||
"klockee,nnesetto,LupusE,",
|
||||
"argilo,dc2dc,formtapez,",
|
||||
"arneluehrs,mcules,rascafr,",
|
||||
"joyel24,ImDroided,zigad,",
|
||||
"johnelder,klockee,nnesetto,",
|
||||
"LupusE,argilo,dc2dc,formtapez,",
|
||||
"RocketGod-git,mrmookie,",
|
||||
"ITAxReal,F33RNI,F4GEV,",
|
||||
"rusty-labs,mjwaxios,andrej-mk,",
|
||||
"RedFox-Fr,nemanjan00,",
|
||||
"MichalLeonBorsuk,",
|
||||
"MatiasFernandez,Giorgiofox,",
|
||||
"ckuethe",
|
||||
"MatiasFernandez,Giorgiofox",
|
||||
" ",
|
||||
"#Havoc:",
|
||||
"jboone,furrtek,eried,argilo,",
|
||||
@@ -72,7 +75,7 @@ AboutView::AboutView(NavigationView& nav) {
|
||||
button_ok.focus();
|
||||
};
|
||||
|
||||
for (auto& authors_line : authors_list) {
|
||||
for (auto& authors_line : mayhem_information_list) {
|
||||
// if it's starting with #, it's a title and we have to substract the '#' and paint yellow
|
||||
if (authors_line.size() > 0) {
|
||||
if (authors_line[0] == '#') {
|
||||
@@ -103,15 +106,13 @@ void AboutView::on_frame_sync() {
|
||||
menu_view.set_highlighted(current + 1);
|
||||
} else {
|
||||
menu_view.set_highlighted(0);
|
||||
// ^ to go back to the REAL top instead of make the 2 at the top to make the title disappeares
|
||||
menu_view.set_highlighted(2); // the first line that has human name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::focus() {
|
||||
button_ok.focus();
|
||||
menu_view.set_highlighted(2); // the first line that has human name
|
||||
menu_view.set_highlighted(3); // contributors block starting line
|
||||
}
|
||||
|
||||
bool AboutView::on_touch(const TouchEvent) {
|
||||
|
||||
@@ -99,13 +99,21 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
|
||||
field_frequency.set_value(aprs_rx_freq);
|
||||
} else if (i == 1) { // NA - North America - is also the default
|
||||
field_frequency.set_value(144390000);
|
||||
} else if (i == 2) { // EUR
|
||||
field_frequency.set_value(144800000);
|
||||
} else if (i == 3) { // AUS
|
||||
field_frequency.set_value(145175000);
|
||||
} else if (i == 4) { // NZ
|
||||
} else if (i == 2) { // NZ
|
||||
field_frequency.set_value(144575000);
|
||||
} else if (i == 5) { // ISS
|
||||
} else if (i == 3) { // JAP
|
||||
field_frequency.set_value(144640000);
|
||||
} else if (i == 4) { // PHI
|
||||
field_frequency.set_value(144740000);
|
||||
} else if (i == 5) { // EUR
|
||||
field_frequency.set_value(144800000);
|
||||
} else if (i == 6) { // THA
|
||||
field_frequency.set_value(144900000);
|
||||
} else if (i == 7) { // AUS
|
||||
field_frequency.set_value(145175000);
|
||||
} else if (i == 8) { // BR
|
||||
field_frequency.set_value(145570000);
|
||||
} else if (i == 9) { // ISS
|
||||
field_frequency.set_value(145825000);
|
||||
}
|
||||
options_region_id = i;
|
||||
|
||||
@@ -227,10 +227,14 @@ class APRSRxView : public View {
|
||||
3,
|
||||
{{"MAN", 0},
|
||||
{"NA ", 1},
|
||||
{"EUR", 2},
|
||||
{"AUS", 3},
|
||||
{"NZ ", 4},
|
||||
{"ISS", 5}}};
|
||||
{"NZ ", 2},
|
||||
{"JAP", 3},
|
||||
{"PHI", 4},
|
||||
{"EUR", 5},
|
||||
{"THA", 6},
|
||||
{"AUS", 7},
|
||||
{"BR ", 8},
|
||||
{"ISS", 9}}};
|
||||
|
||||
FrequencyField field_frequency{
|
||||
{3 * 8, 0 * 16}};
|
||||
|
||||
@@ -177,4 +177,4 @@ void FlashUtilityView::focus() {
|
||||
menu_view.focus();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui */
|
||||
|
||||
@@ -711,10 +711,12 @@ SetEncoderDialView::SetEncoderDialView(NavigationView& nav) {
|
||||
&button_dial_sensitivity_plus,
|
||||
&button_dial_sensitivity_minus,
|
||||
&button_rate_multiplier_plus,
|
||||
&button_rate_multiplier_minus});
|
||||
&button_rate_multiplier_minus,
|
||||
&field_encoder_dial_direction});
|
||||
|
||||
field_encoder_dial_sensitivity.set_by_value(pmem::encoder_dial_sensitivity());
|
||||
field_encoder_rate_multiplier.set_value(pmem::encoder_rate_multiplier());
|
||||
field_encoder_dial_direction.set_by_value(pmem::encoder_dial_direction());
|
||||
|
||||
button_dial_sensitivity_plus.on_select = [this](Button&) {
|
||||
field_encoder_dial_sensitivity.on_encoder(1);
|
||||
@@ -732,6 +734,7 @@ SetEncoderDialView::SetEncoderDialView(NavigationView& nav) {
|
||||
button_save.on_select = [&nav, this](Button&) {
|
||||
pmem::set_encoder_dial_sensitivity(field_encoder_dial_sensitivity.selected_index_value());
|
||||
pmem::set_encoder_rate_multiplier(field_encoder_rate_multiplier.value());
|
||||
pmem::set_encoder_dial_direction(field_encoder_dial_direction.selected_index_value());
|
||||
nav.pop();
|
||||
};
|
||||
|
||||
|
||||
@@ -574,6 +574,7 @@ class SetQRCodeView : public View {
|
||||
};
|
||||
};
|
||||
|
||||
using portapack::persistent_memory::encoder_dial_direction;
|
||||
using portapack::persistent_memory::encoder_dial_sensitivity;
|
||||
using portapack::persistent_memory::encoder_rate_multiplier;
|
||||
|
||||
@@ -587,44 +588,50 @@ class SetEncoderDialView : public View {
|
||||
|
||||
private:
|
||||
Labels labels{
|
||||
{{1 * 8, 1 * 16}, "Adjusts sensitivity to dial", Theme::getInstance()->fg_light->foreground},
|
||||
{{1 * 8, 2 * 16}, "rotation position (number of", Theme::getInstance()->fg_light->foreground},
|
||||
{{1 * 8, 3 * 16}, "steps per full rotation):", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 5 * 16}, "Dial sensitivity:", Theme::getInstance()->fg_light->foreground},
|
||||
{{1 * 8, 8 * 16}, "Adjusts sensitivity to dial", Theme::getInstance()->fg_light->foreground},
|
||||
{{1 * 8, 9 * 16}, "rotation rate (default 1", Theme::getInstance()->fg_light->foreground},
|
||||
{{1 * 8, 10 * 16}, "means no rate dependency):", Theme::getInstance()->fg_light->foreground},
|
||||
{{3 * 8, 12 * 16}, "Rate multiplier:", Theme::getInstance()->fg_light->foreground},
|
||||
{{0 * 8, 0 * 16}, "Sensitivity to dial rotation", Theme::getInstance()->fg_light->foreground},
|
||||
{{0 * 8, 1 * 16}, "position (x steps per 360):", Theme::getInstance()->fg_light->foreground},
|
||||
{{1 * 8, 3 * 16}, "Sensitivity:", Theme::getInstance()->fg_light->foreground},
|
||||
{{0 * 8, 7 * 16}, "Rotation rate (default 1", Theme::getInstance()->fg_light->foreground},
|
||||
{{0 * 8, 8 * 16}, "means no rate dependency):", Theme::getInstance()->fg_light->foreground},
|
||||
{{2 * 8, 10 * 16}, "Rate multiplier:", Theme::getInstance()->fg_light->foreground},
|
||||
{{4 * 8, 14 * 16}, "Direction:", Theme::getInstance()->fg_light->foreground},
|
||||
|
||||
};
|
||||
|
||||
OptionsField field_encoder_dial_sensitivity{
|
||||
{20 * 8, 5 * 16},
|
||||
{20 * 8, 3 * 16},
|
||||
6,
|
||||
{{"LOW", encoder_dial_sensitivity::DIAL_SENSITIVITY_LOW},
|
||||
{"NORMAL", encoder_dial_sensitivity::DIAL_SENSITIVITY_NORMAL},
|
||||
{"HIGH", encoder_dial_sensitivity::DIAL_SENSITIVITY_HIGH}}};
|
||||
|
||||
NumberField field_encoder_rate_multiplier{
|
||||
{20 * 8, 12 * 16},
|
||||
{20 * 8, 10 * 16},
|
||||
2,
|
||||
{1, 15},
|
||||
1,
|
||||
' '};
|
||||
|
||||
OptionsField field_encoder_dial_direction{
|
||||
{18 * 8, 14 * 16},
|
||||
7,
|
||||
{{"NORMAL", false},
|
||||
{"REVERSE", true}}};
|
||||
|
||||
Button button_dial_sensitivity_plus{
|
||||
{20 * 8, 4 * 16, 16, 16},
|
||||
{20 * 8, 2 * 16, 16, 16},
|
||||
"+"};
|
||||
|
||||
Button button_dial_sensitivity_minus{
|
||||
{20 * 8, 6 * 16, 16, 16},
|
||||
{20 * 8, 4 * 16, 16, 16},
|
||||
"-"};
|
||||
|
||||
Button button_rate_multiplier_plus{
|
||||
{20 * 8, 11 * 16, 16, 16},
|
||||
{20 * 8, 9 * 16, 16, 16},
|
||||
"+"};
|
||||
|
||||
Button button_rate_multiplier_minus{
|
||||
{20 * 8, 13 * 16, 16, 16},
|
||||
{20 * 8, 11 * 16, 16, 16},
|
||||
"-"};
|
||||
|
||||
Button button_save{
|
||||
|
||||
@@ -44,9 +44,9 @@ SigGenView::~SigGenView() {
|
||||
|
||||
void SigGenView::update_config() {
|
||||
if (checkbox_stop.value())
|
||||
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), field_stop.value());
|
||||
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), (options_mod.selected_index_value() << 4) + options_shape.selected_index_value(), field_stop.value());
|
||||
else
|
||||
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), 0);
|
||||
baseband::set_siggen_config(transmitter_model.channel_bandwidth(), (options_mod.selected_index_value() << 4) + options_shape.selected_index_value(), 0);
|
||||
}
|
||||
|
||||
void SigGenView::update_tone() {
|
||||
@@ -78,6 +78,7 @@ SigGenView::SigGenView(
|
||||
baseband::run_image(portapack::spi_flash::image_tag_siggen);
|
||||
|
||||
add_children({&labels,
|
||||
&options_mod,
|
||||
&options_shape,
|
||||
&text_shape,
|
||||
&symfield_tone,
|
||||
@@ -87,22 +88,48 @@ SigGenView::SigGenView(
|
||||
&field_stop,
|
||||
&tx_view});
|
||||
|
||||
symfield_tone.hidden(1); // At first launch , by default we are in CW Shape has NO MOD , we are not using Tone modulation.
|
||||
symfield_tone.hidden(true); // At first launch , by default we are in CW: Shape ignored, we are not using Tone modulation.
|
||||
options_shape.hidden(true);
|
||||
text_shape.hidden(true);
|
||||
symfield_tone.set_value(1000); // Default: 1000 Hz
|
||||
options_shape.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
text_shape.set(shape_strings[v]);
|
||||
if (auto_update)
|
||||
update_config();
|
||||
if ((v == 0) || (v == 6)) { // In Shapes Options (CW & Pseudo Random Noise) we are not using Tone modulation freq.
|
||||
symfield_tone.hidden(1);
|
||||
|
||||
if (v == 5) { // In Shape Pseudo Random Noise we are not using Tone modulation freq.
|
||||
symfield_tone.hidden(true);
|
||||
} else {
|
||||
symfield_tone.hidden(0);
|
||||
symfield_tone.hidden(false);
|
||||
}
|
||||
|
||||
set_dirty();
|
||||
};
|
||||
options_shape.set_selected_index(0);
|
||||
text_shape.set(shape_strings[0]);
|
||||
|
||||
options_mod.on_change = [this](size_t, OptionsField::value_t v) {
|
||||
if (auto_update)
|
||||
update_config();
|
||||
|
||||
if (v == 0) { // In Modulation Options CW we are not using Tone modulation freq.
|
||||
symfield_tone.hidden(true);
|
||||
} else {
|
||||
symfield_tone.hidden(false);
|
||||
}
|
||||
|
||||
if ((v == 0) || (v == 2) || (v == 3)) { // In Modulation Options CW, QPSK, BPSK we are not using Shapes.
|
||||
options_shape.hidden(true);
|
||||
text_shape.hidden(true);
|
||||
} else {
|
||||
options_shape.hidden(false);
|
||||
text_shape.hidden(false);
|
||||
}
|
||||
|
||||
set_dirty();
|
||||
};
|
||||
options_mod.set_selected_index(0);
|
||||
|
||||
field_stop.set_value(1);
|
||||
|
||||
symfield_tone.set_value(1000); // Default: 1000 Hz
|
||||
|
||||
@@ -58,68 +58,73 @@ class SigGenView : public View {
|
||||
app_settings::SettingsManager settings_{
|
||||
"tx_siggen", app_settings::Mode::TX};
|
||||
|
||||
const std::string shape_strings[9] = {
|
||||
"CW (No mod.) ",
|
||||
"Sine mod. FM",
|
||||
"Triangle mod.FM", // max 15 character text space.
|
||||
"Saw up mod. FM",
|
||||
"Saw down mod.FM",
|
||||
"Square mod. FM",
|
||||
"Pseudo Noise FM", // using 16 bits LFSR register, 16 order polynomial feedback.
|
||||
"BPSK 0,1,0,1...",
|
||||
"QPSK 00-01-10.."};
|
||||
const std::string shape_strings[6] = {// max 15 character text space.
|
||||
"Sine",
|
||||
"Triangle",
|
||||
"Saw up",
|
||||
"Saw down",
|
||||
"Square",
|
||||
"Pseudo Noise"};
|
||||
|
||||
bool auto_update{false};
|
||||
|
||||
Labels labels{
|
||||
{{3 * 8, 4 + 10}, "Shape:", Theme::getInstance()->fg_light->foreground},
|
||||
{{6 * 8, 7 * 8}, "Tone: Hz", Theme::getInstance()->fg_light->foreground},
|
||||
{{22 * 8, 15 * 8 + 4}, "s.", Theme::getInstance()->fg_light->foreground},
|
||||
{{8 * 8, 20 * 8}, "Modulation: FM", Theme::getInstance()->fg_light->foreground}};
|
||||
{{3 * 8, 2 * 8}, "Modulation:", Theme::getInstance()->fg_light->foreground},
|
||||
{{3 * 8, 3 * 8 + 8 + 10}, "Shape:", Theme::getInstance()->fg_light->foreground},
|
||||
{{6 * 8, 2 * 8 + 7 * 8}, "Tone: Hz", Theme::getInstance()->fg_light->foreground},
|
||||
{{22 * 8, 2 * 8 + 15 * 8 + 4}, "s.", Theme::getInstance()->fg_light->foreground}};
|
||||
|
||||
ImageOptionsField options_shape{
|
||||
{10 * 8, 4, 32, 32},
|
||||
{10 * 8, 3 * 8 + 8, 32, 32},
|
||||
Theme::getInstance()->bg_darkest->foreground,
|
||||
Theme::getInstance()->bg_darkest->background,
|
||||
{{&bitmap_sig_cw, 0},
|
||||
{&bitmap_sig_sine, 1},
|
||||
{&bitmap_sig_tri, 2},
|
||||
{&bitmap_sig_saw_up, 3},
|
||||
{&bitmap_sig_saw_down, 4},
|
||||
{&bitmap_sig_square, 5},
|
||||
{&bitmap_sig_noise, 6},
|
||||
{&bitmap_sig_noise, 7}, // Pending to add a correct BPSK icon.
|
||||
{&bitmap_sig_noise, 8}}}; // Pending to add a correct QPSK icon.
|
||||
{{&bitmap_sig_sine, 0},
|
||||
{&bitmap_sig_tri, 1},
|
||||
{&bitmap_sig_saw_up, 2},
|
||||
{&bitmap_sig_saw_down, 3},
|
||||
{&bitmap_sig_square, 4},
|
||||
{&bitmap_sig_noise, 5}}};
|
||||
|
||||
Text text_shape{
|
||||
{15 * 8, 4 + 10, 15 * 8, 16},
|
||||
{15 * 8, 3 * 8 + 8 + 10, 15 * 8, 16},
|
||||
""};
|
||||
|
||||
SymField symfield_tone{
|
||||
{12 * 8, 7 * 8},
|
||||
{12 * 8, 2 * 8 + 7 * 8},
|
||||
5};
|
||||
|
||||
Button button_update{
|
||||
{5 * 8, 10 * 8, 8 * 8, 3 * 8},
|
||||
{5 * 8, 2 * 8 + 10 * 8, 8 * 8, 3 * 8},
|
||||
"Update"};
|
||||
|
||||
Checkbox checkbox_auto{
|
||||
{15 * 8, 10 * 8},
|
||||
{15 * 8, 2 * 8 + 10 * 8},
|
||||
4,
|
||||
"Auto"};
|
||||
|
||||
Checkbox checkbox_stop{
|
||||
{5 * 8, 15 * 8},
|
||||
{5 * 8, 2 * 8 + 15 * 8},
|
||||
10,
|
||||
"Stop after"};
|
||||
|
||||
NumberField field_stop{
|
||||
{20 * 8, 15 * 8 + 4},
|
||||
{20 * 8, 2 * 8 + 15 * 8 + 4},
|
||||
2,
|
||||
{1, 99},
|
||||
1,
|
||||
' '};
|
||||
|
||||
OptionsField options_mod{
|
||||
{15 * 8, 2 * 8},
|
||||
12,
|
||||
{{"CW (No mod.)", 0},
|
||||
{"FM", 1},
|
||||
{"BPSK", 2},
|
||||
{"QPSK", 3},
|
||||
{"DSB", 4},
|
||||
{"AM 100% dep.", 5},
|
||||
{"AM 50% depth", 6}}};
|
||||
|
||||
TransmitterView tx_view{
|
||||
16 * 16,
|
||||
10000,
|
||||
|
||||
@@ -987,44 +987,6 @@ static constexpr Bitmap bitmap_icon_previous{
|
||||
{16, 16},
|
||||
bitmap_icon_previous_data};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_tools_antenna_data[] = {
|
||||
0x38,
|
||||
0x3E,
|
||||
0x10,
|
||||
0x22,
|
||||
0x10,
|
||||
0x26,
|
||||
0x10,
|
||||
0x22,
|
||||
0x10,
|
||||
0x2E,
|
||||
0x10,
|
||||
0x22,
|
||||
0x10,
|
||||
0x26,
|
||||
0x10,
|
||||
0x22,
|
||||
0x38,
|
||||
0x2E,
|
||||
0x38,
|
||||
0x22,
|
||||
0x38,
|
||||
0x26,
|
||||
0x38,
|
||||
0x22,
|
||||
0x38,
|
||||
0x2E,
|
||||
0x38,
|
||||
0x22,
|
||||
0x38,
|
||||
0x3E,
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
static constexpr Bitmap bitmap_icon_tools_antenna{
|
||||
{16, 16},
|
||||
bitmap_icon_tools_antenna_data};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_batt_text_data[] = {
|
||||
0x00,
|
||||
0x00,
|
||||
@@ -1799,6 +1761,45 @@ static constexpr uint8_t bitmap_icon_lge_data[] = {
|
||||
0x00,
|
||||
0x00,
|
||||
};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_file_iq_data[] = {
|
||||
0xFC,
|
||||
0x03,
|
||||
0x04,
|
||||
0x06,
|
||||
0x04,
|
||||
0x0E,
|
||||
0x04,
|
||||
0x1E,
|
||||
0x04,
|
||||
0x3E,
|
||||
0x04,
|
||||
0x20,
|
||||
0x04,
|
||||
0x20,
|
||||
0x04,
|
||||
0x21,
|
||||
0x44,
|
||||
0x25,
|
||||
0x54,
|
||||
0x25,
|
||||
0xF4,
|
||||
0x2F,
|
||||
0xA4,
|
||||
0x2A,
|
||||
0x84,
|
||||
0x22,
|
||||
0x04,
|
||||
0x22,
|
||||
0x04,
|
||||
0x20,
|
||||
0xFC,
|
||||
0x3F,
|
||||
};
|
||||
static constexpr Bitmap bitmap_icon_file_iq{
|
||||
{16, 16},
|
||||
bitmap_icon_file_iq_data};
|
||||
|
||||
static constexpr Bitmap bitmap_icon_lge{
|
||||
{16, 16},
|
||||
bitmap_icon_lge_data};
|
||||
@@ -2713,44 +2714,6 @@ static constexpr Bitmap bitmap_icon_touchtunes{
|
||||
{16, 16},
|
||||
bitmap_icon_touchtunes_data};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_file_iq_data[] = {
|
||||
0xFC,
|
||||
0x03,
|
||||
0x04,
|
||||
0x06,
|
||||
0x04,
|
||||
0x0E,
|
||||
0x04,
|
||||
0x1E,
|
||||
0x04,
|
||||
0x3E,
|
||||
0x04,
|
||||
0x20,
|
||||
0x04,
|
||||
0x20,
|
||||
0x04,
|
||||
0x21,
|
||||
0x44,
|
||||
0x25,
|
||||
0x54,
|
||||
0x25,
|
||||
0xF4,
|
||||
0x2F,
|
||||
0xA4,
|
||||
0x2A,
|
||||
0x84,
|
||||
0x22,
|
||||
0x04,
|
||||
0x22,
|
||||
0x04,
|
||||
0x20,
|
||||
0xFC,
|
||||
0x3F,
|
||||
};
|
||||
static constexpr Bitmap bitmap_icon_file_iq{
|
||||
{16, 16},
|
||||
bitmap_icon_file_iq_data};
|
||||
|
||||
static constexpr uint8_t bitmap_icon_sd_data[] = {
|
||||
0x00,
|
||||
0x00,
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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_whipcalc.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::antenna_length {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<WhipCalcView>();
|
||||
}
|
||||
} // namespace ui::external_app::antenna_length
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_antenna_length.application_information"), used)) application_information_t _application_information_antenna_length = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::antenna_length::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "Antenna Length",
|
||||
/*.bitmap_data = */ {0x38, 0x3E, 0x10, 0x22, 0x10, 0x26, 0x10, 0x22, 0x10, 0x2E, 0x10, 0x22, 0x10, 0x26, 0x10, 0x22, 0x38, 0x2E, 0x38, 0x22, 0x38, 0x26, 0x38, 0x22, 0x38, 0x2E, 0x38, 0x22, 0x38, 0x3E, 0x00, 0x00},
|
||||
/*.icon_color = */ ui::Color::cyan().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
+3
-2
@@ -32,8 +32,9 @@
|
||||
#include <cstring>
|
||||
|
||||
using namespace portapack;
|
||||
using namespace ui;
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::antenna_length {
|
||||
|
||||
void WhipCalcView::focus() {
|
||||
field_frequency.focus();
|
||||
@@ -176,4 +177,4 @@ void WhipCalcView::load_antenna_db() {
|
||||
void WhipCalcView::add_default_antenna() {
|
||||
antenna_db.push_back({"ANT500", {185, 315, 450, 586, 724, 862}}); // store a default ant500
|
||||
}
|
||||
} // namespace ui
|
||||
} // namespace ui::external_app::antenna_length
|
||||
+5
-2
@@ -31,7 +31,10 @@
|
||||
#include "string_format.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace ui {
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::antenna_length {
|
||||
|
||||
class WhipCalcView : public View {
|
||||
public:
|
||||
WhipCalcView(NavigationView& nav);
|
||||
@@ -90,6 +93,6 @@ class WhipCalcView : public View {
|
||||
"Back"};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
} // namespace ui::external_app::antenna_length
|
||||
|
||||
#endif /*__UI_WHIPCALC__*/
|
||||
+16
-1
@@ -150,6 +150,18 @@ set(EXTCPPSRC
|
||||
#app_manager
|
||||
external/app_manager/main.cpp
|
||||
external/app_manager/ui_app_manager.cpp
|
||||
|
||||
# whip calculator
|
||||
external/antenna_length/main.cpp
|
||||
external/antenna_length/ui_whipcalc.cpp
|
||||
|
||||
# wav viewer
|
||||
external/wav_view/main.cpp
|
||||
external/wav_view/ui_view_wav.cpp
|
||||
|
||||
# wipe sdcard
|
||||
external/sd_wipe/main.cpp
|
||||
external/sd_wipe/ui_sd_wipe.cpp
|
||||
)
|
||||
|
||||
set(EXTAPPLIST
|
||||
@@ -189,4 +201,7 @@ set(EXTAPPLIST
|
||||
tuner
|
||||
metronome
|
||||
app_manager
|
||||
)
|
||||
antenna_length
|
||||
view_wav
|
||||
sd_wipe
|
||||
)
|
||||
|
||||
+21
@@ -59,6 +59,9 @@ MEMORY
|
||||
ram_external_app_tuner(rwx) : org = 0xADD20000, len = 32k
|
||||
ram_external_app_metronome(rwx) : org = 0xADD30000, len = 32k
|
||||
ram_external_app_app_manager(rwx) : org = 0xADD40000, len = 32k
|
||||
ram_external_app_antenna_length(rwx) : org = 0xADD50000, len = 32k
|
||||
ram_external_app_view_wav(rwx) : org = 0xADD60000, len = 32k
|
||||
ram_external_app_sd_wipe(rwx) : org = 0xADD70000, len = 32k
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
@@ -279,4 +282,22 @@ SECTIONS
|
||||
KEEP(*(.external_app.app_app_manager.application_information));
|
||||
*(*ui*external_app*app_manager*);
|
||||
} > ram_external_app_app_manager
|
||||
|
||||
.external_app_antenna_length : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_antenna_length.application_information));
|
||||
*(*ui*external_app*antenna_length*);
|
||||
} > ram_external_app_antenna_length
|
||||
|
||||
.external_app_view_wav : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_view_wav.application_information));
|
||||
*(*ui*external_app*view_wav*);
|
||||
} > ram_external_app_view_wav
|
||||
|
||||
.external_app_sd_wipe : ALIGN(4) SUBALIGN(4)
|
||||
{
|
||||
KEEP(*(.external_app.app_sd_wipe.application_information));
|
||||
*(*ui*external_app*sd_wipe*);
|
||||
} > ram_external_app_sd_wipe
|
||||
}
|
||||
|
||||
@@ -89,8 +89,6 @@ class FlipperTxView : public View {
|
||||
std::unique_ptr<FlipperPlayThread> replay_thread{};
|
||||
Optional<flippersub_metadata> submeta{};
|
||||
|
||||
const std::filesystem::path subghz_dir = u"subghz";
|
||||
|
||||
MessageHandlerRegistration message_handler_tx_progress{
|
||||
Message::ID::TXProgress,
|
||||
[this](const Message* const p) {
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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_sd_wipe.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::sd_wipe {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<WipeSDView>();
|
||||
}
|
||||
} // namespace ui::external_app::sd_wipe
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_sd_wipe.application_information"), used)) application_information_t _application_information_sd_wipe = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::sd_wipe::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "Wipe SD card",
|
||||
/*.bitmap_data = */ {0xF0, 0x3F, 0x58, 0x35, 0x5C, 0x35, 0xFC, 0x3F, 0xFC, 0x3F, 0xFC, 0x3F, 0x3C, 0x1C, 0xBC, 0xC9, 0xBC, 0xE3, 0x2C, 0x77, 0x5C, 0x3E, 0xAC, 0x1C, 0x5C, 0x3E, 0x2C, 0x77, 0x9C, 0xE3, 0xAC, 0xC1},
|
||||
/*.icon_color = */ ui::Color::red().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {0, 0, 0, 0},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
Vendored
+4
-2
@@ -22,7 +22,9 @@
|
||||
|
||||
#include "ui_sd_wipe.hpp"
|
||||
|
||||
namespace ui {
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::sd_wipe {
|
||||
|
||||
Thread* WipeSDView::thread{nullptr};
|
||||
|
||||
@@ -63,4 +65,4 @@ void WipeSDView::focus() {
|
||||
}
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} // namespace ui::external_app::sd_wipe
|
||||
Vendored
+4
-2
@@ -30,7 +30,9 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace ui {
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::sd_wipe {
|
||||
|
||||
class WipeSDView : public View {
|
||||
public:
|
||||
@@ -87,6 +89,6 @@ class WipeSDView : public View {
|
||||
""};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
} // namespace ui::external_app::sd_wipe
|
||||
|
||||
#endif /*__UI_SD_WIPE_H__*/
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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_view_wav.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "external_app.hpp"
|
||||
|
||||
namespace ui::external_app::view_wav {
|
||||
void initialize_app(ui::NavigationView& nav) {
|
||||
nav.push<ViewWavView>();
|
||||
}
|
||||
} // namespace ui::external_app::view_wav
|
||||
|
||||
extern "C" {
|
||||
|
||||
__attribute__((section(".external_app.app_view_wav.application_information"), used)) application_information_t _application_information_view_wav = {
|
||||
/*.memory_location = */ (uint8_t*)0x00000000,
|
||||
/*.externalAppEntry = */ ui::external_app::view_wav::initialize_app,
|
||||
/*.header_version = */ CURRENT_HEADER_VERSION,
|
||||
/*.app_version = */ VERSION_MD5,
|
||||
|
||||
/*.app_name = */ "WAV Viewer",
|
||||
/*.bitmap_data = */ {0xF0, 0x0F, 0x1C, 0x18, 0x17, 0x38, 0x15, 0x78, 0x15, 0xF8, 0x15, 0x82, 0x15, 0x8B, 0xD5, 0x83, 0xD5, 0xBB, 0xD5, 0x83, 0x15, 0x8B, 0x15, 0x92, 0x15, 0xA0, 0x17, 0x80, 0x1C, 0x80, 0xF0, 0xFF},
|
||||
/*.icon_color = */ ui::Color::green().v,
|
||||
/*.menu_location = */ app_location_t::UTILITIES,
|
||||
/*.desired_menu_position = */ -1,
|
||||
|
||||
/*.m4_app_tag = portapack::spi_flash::image_tag_none */ {'P', 'A', 'T', 'X'},
|
||||
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
|
||||
};
|
||||
}
|
||||
Vendored
+3
-2
@@ -28,8 +28,9 @@
|
||||
#include "string_format.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
using namespace ui;
|
||||
|
||||
namespace ui {
|
||||
namespace ui::external_app::view_wav {
|
||||
|
||||
void ViewWavView::update_scale(int32_t new_scale) {
|
||||
scale = new_scale;
|
||||
@@ -345,4 +346,4 @@ ViewWavView::~ViewWavView() {
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui::external_app::view_wav */
|
||||
Vendored
+4
-2
@@ -28,7 +28,9 @@
|
||||
#include "ui_receiver.hpp"
|
||||
#include "replay_thread.hpp"
|
||||
|
||||
namespace ui {
|
||||
using namespace ui;
|
||||
|
||||
namespace ui::external_app::view_wav {
|
||||
|
||||
class ViewWavView : public View {
|
||||
public:
|
||||
@@ -202,4 +204,4 @@ class ViewWavView : public View {
|
||||
}};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
} /* namespace ui::external_app::view_wav */
|
||||
@@ -49,3 +49,4 @@ const std::filesystem::path sstv_dir = u"SSTV";
|
||||
const std::filesystem::path wav_dir = u"WAV";
|
||||
const std::filesystem::path whipcalc_dir = u"WHIPCALC";
|
||||
const std::filesystem::path ook_editor_dir = u"OOKFILES";
|
||||
const std::filesystem::path subghz_dir = u"SUBGHZ";
|
||||
|
||||
@@ -51,5 +51,6 @@ extern const std::filesystem::path sstv_dir;
|
||||
extern const std::filesystem::path wav_dir;
|
||||
extern const std::filesystem::path whipcalc_dir;
|
||||
extern const std::filesystem::path ook_editor_dir;
|
||||
extern const std::filesystem::path subghz_dir;
|
||||
|
||||
#endif /* __FILE_PATH_H__ */
|
||||
|
||||
@@ -66,6 +66,11 @@ int_fast8_t Encoder::update(const uint_fast8_t phase_bits) {
|
||||
if (direction == prev_direction) {
|
||||
if ((sensitivity_map[portapack::persistent_memory::encoder_dial_sensitivity()] & (1 << state)) == 0)
|
||||
return 0;
|
||||
|
||||
// false: normal, true: reverse
|
||||
if (portapack::persistent_memory::encoder_dial_direction())
|
||||
direction = -direction;
|
||||
|
||||
return direction;
|
||||
}
|
||||
|
||||
|
||||
@@ -110,12 +110,12 @@ void BtnGridView::set_arrow_up_enabled(bool enabled) {
|
||||
if (enabled) {
|
||||
if (!arrow_up_enabled) {
|
||||
arrow_up_enabled = true;
|
||||
button_pgup.set_text("PAGE UP");
|
||||
button_pgup.set_text("< PREV");
|
||||
}
|
||||
} else if (!enabled) {
|
||||
if (arrow_up_enabled) {
|
||||
arrow_up_enabled = false;
|
||||
button_pgup.set_text(" ");
|
||||
button_pgup.set_text(" ");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -126,12 +126,12 @@ void BtnGridView::set_arrow_down_enabled(bool enabled) {
|
||||
if (enabled) {
|
||||
if (!arrow_down_enabled) {
|
||||
arrow_down_enabled = true;
|
||||
button_pgdown.set_text("PAGE DOWN");
|
||||
button_pgdown.set_text("NEXT >");
|
||||
}
|
||||
} else if (!enabled) {
|
||||
if (arrow_down_enabled) {
|
||||
arrow_down_enabled = false;
|
||||
button_pgdown.set_text(" ");
|
||||
button_pgdown.set_text(" ");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -228,6 +228,10 @@ NewButton* BtnGridView::item_view(size_t index) const {
|
||||
|
||||
void BtnGridView::show_arrows_enabled(bool enabled) {
|
||||
show_arrows = enabled;
|
||||
if (!enabled) {
|
||||
remove_child(&button_pgup);
|
||||
remove_child(&button_pgdown);
|
||||
}
|
||||
}
|
||||
|
||||
bool BtnGridView::set_highlighted(int32_t new_value) {
|
||||
|
||||
@@ -54,7 +54,6 @@
|
||||
#include "ui_recon.hpp"
|
||||
#include "ui_scanner.hpp"
|
||||
#include "ui_sd_over_usb.hpp"
|
||||
#include "ui_sd_wipe.hpp"
|
||||
#include "ui_search.hpp"
|
||||
#include "ui_settings.hpp"
|
||||
#include "ui_siggen.hpp"
|
||||
@@ -63,10 +62,8 @@
|
||||
// #include "ui_test.hpp"
|
||||
#include "ui_text_editor.hpp"
|
||||
#include "ui_touchtunes.hpp"
|
||||
#include "ui_view_wav.hpp"
|
||||
#include "ui_weatherstation.hpp"
|
||||
#include "ui_subghzd.hpp"
|
||||
#include "ui_whipcalc.hpp"
|
||||
#include "ui_battinfo.hpp"
|
||||
#include "ui_external_items_menu_loader.hpp"
|
||||
|
||||
@@ -153,7 +150,6 @@ const NavigationView::AppList NavigationView::appList = {
|
||||
//{"sstv", "SSTV", RX, Color::dark_grey(), &bitmap_icon_sstv, new ViewFactory<NotImplementedView>()},
|
||||
//{"tetra", "TETRA", RX, Color::dark_grey(), &bitmap_icon_tetra, new ViewFactory<NotImplementedView>()},
|
||||
/* TX ********************************************************************/
|
||||
//{"adsbtx", "ADS-B TX", TX, ui::Color::green(), &bitmap_icon_adsb, new ViewFactory<ADSBTxView>()},
|
||||
{"aprstx", "APRS TX", TX, ui::Color::green(), &bitmap_icon_aprs, new ViewFactory<APRSTXView>()},
|
||||
{"bht", "BHT Xy/EP", TX, ui::Color::green(), &bitmap_icon_bht, new ViewFactory<BHTView>()},
|
||||
{"bletx", "BLE Tx", TX, ui::Color::green(), &bitmap_icon_btle, new ViewFactory<BLETxView>()},
|
||||
@@ -163,19 +159,15 @@ const NavigationView::AppList NavigationView::appList = {
|
||||
{"soundbrd", "Soundbrd", TX, ui::Color::green(), &bitmap_icon_soundboard, new ViewFactory<SoundBoardView>()},
|
||||
{"touchtune", "TouchTune", TX, ui::Color::green(), &bitmap_icon_touchtunes, new ViewFactory<TouchTunesView>()},
|
||||
/* UTILITIES *************************************************************/
|
||||
{"antennalength", "Antenna Length", UTILITIES, Color::green(), &bitmap_icon_tools_antenna, new ViewFactory<WhipCalcView>()},
|
||||
{"filemanager", "File Manager", UTILITIES, Color::green(), &bitmap_icon_dir, new ViewFactory<FileManagerView>()},
|
||||
{"freqman", "Freq. Manager", UTILITIES, Color::green(), &bitmap_icon_freqman, new ViewFactory<FrequencyManagerView>()},
|
||||
{"notepad", "Notepad", UTILITIES, Color::dark_cyan(), &bitmap_icon_notepad, new ViewFactory<TextEditorView>()},
|
||||
{"iqtrim", "IQ Trim", UTILITIES, Color::orange(), &bitmap_icon_trim, new ViewFactory<IQTrimView>()},
|
||||
{"notepad", "Notepad", UTILITIES, Color::dark_cyan(), &bitmap_icon_notepad, new ViewFactory<TextEditorView>()},
|
||||
{nullptr, "SD Over USB", UTILITIES, Color::yellow(), &bitmap_icon_hackrf, new ViewFactory<SdOverUsbView>()},
|
||||
{"signalgen", "Signal Gen", UTILITIES, Color::green(), &bitmap_icon_cwgen, new ViewFactory<SigGenView>()},
|
||||
//{"testapp", "Test App", UTILITIES, Color::dark_grey(), nullptr, new ViewFactory<TestView>()},
|
||||
|
||||
{"wavview", "Wav View", UTILITIES, Color::yellow(), &bitmap_icon_soundboard, new ViewFactory<ViewWavView>()},
|
||||
// Dangerous apps.
|
||||
{nullptr, "Flash Utility", UTILITIES, Color::red(), &bitmap_icon_peripherals_details, new ViewFactory<FlashUtilityView>()},
|
||||
{nullptr, "Wipe SD card", UTILITIES, Color::red(), &bitmap_icon_tools_wipesd, new ViewFactory<WipeSDView>()},
|
||||
};
|
||||
|
||||
const NavigationView::AppMap NavigationView::appMap = generate_app_map(NavigationView::appList);
|
||||
|
||||
@@ -38,28 +38,67 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
|
||||
} else
|
||||
sample_count--;
|
||||
|
||||
if (tone_shape == 0) {
|
||||
if (modulation == 0) {
|
||||
// CW
|
||||
re = 127; // max. signed 8 bits value . (-128 ...+127), max. amplitude , static phasor at 0º
|
||||
im = 0;
|
||||
} else {
|
||||
if (tone_shape == 1) {
|
||||
} else if (modulation == 2) {
|
||||
// Digital BPSK consecutive 0,1,0,...continuous cycle, 1 bit/symbol, at rate of 2 symbols / Freq Tone Periode... without any Pulse shape at the moment .
|
||||
re = (((tone_phase & 0xFF000000) >> 24) & 0x80) ? 127 : -128; // Sending 2 bits by Periode T of the GUI tone, alternative static phasor to 0, -180º , 0º
|
||||
im = 0;
|
||||
tone_phase += tone_delta; // In BPSK-QSPK we are using to calculate each 1/4 of the periode.
|
||||
} else if (modulation == 3) {
|
||||
// Digital QPSK consecutive 00, 01, 10, 11,00, ...continuous cycle ,2 bits/symbol, at rate of 4 symbols / Freq Tone Periode. not random., without any Pulse shape at the moment .
|
||||
|
||||
switch (((tone_phase & 0xFF000000) >> 24)) {
|
||||
case 0 ... 63: // equivalent to 1/4 of total 360º degrees.
|
||||
/* "00" */
|
||||
re = (sine_table_i8[32]); // we are sending symbol-phasor 45º during 1/4 of the total periode
|
||||
im = (sine_table_i8[32 + 64]); // 32 index = rounded (45º/360º * 255 total sin table steps) = 31,875
|
||||
break;
|
||||
|
||||
case 64 ... 127:
|
||||
/* "01" */
|
||||
re = (sine_table_i8[96]); // symbol-phasor 135º
|
||||
im = (sine_table_i8[96 + 64]); // 96 index = 32 + 256/4
|
||||
break;
|
||||
break;
|
||||
|
||||
case 128 ... 191:
|
||||
/* "10" */
|
||||
re = (sine_table_i8[159]); // symbol-phasor 225º
|
||||
im = (sine_table_i8[159 + 64]); // 159 rounded index = 96 + 256/4 = 159.3
|
||||
break;
|
||||
|
||||
case 192 ... 255:
|
||||
/* "11" */
|
||||
re = (sine_table_i8[223]); // symbol-phasor 315º ; 223 rounded index = (315/360) * 255 =223.125
|
||||
im = (sine_table_i8[((223 + 64) & 0xFF)]); // Max index 255, circular periodic conversion.
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
tone_phase += tone_delta; // In BPSK-QSPK we are using to calculate each 1/4 of the periode.
|
||||
|
||||
} else { // Other modulations: FM, DSB, AM
|
||||
if (tone_shape == 0) {
|
||||
// Sine
|
||||
sample = (sine_table_i8[(tone_phase & 0xFF000000) >> 24]);
|
||||
} else if (tone_shape == 2) {
|
||||
} else if (tone_shape == 1) {
|
||||
// Triangle
|
||||
int8_t a = (tone_phase & 0xFF000000) >> 24;
|
||||
sample = (a & 0x80) ? ((a << 1) ^ 0xFF) - 0x80 : (a << 1) + 0x80;
|
||||
} else if (tone_shape == 3) {
|
||||
} else if (tone_shape == 2) {
|
||||
// Saw up
|
||||
sample = ((tone_phase & 0xFF000000) >> 24);
|
||||
} else if (tone_shape == 4) {
|
||||
} else if (tone_shape == 3) {
|
||||
// Saw down
|
||||
sample = ((tone_phase & 0xFF000000) >> 24) ^ 0xFF;
|
||||
} else if (tone_shape == 5) {
|
||||
} else if (tone_shape == 4) {
|
||||
// Square
|
||||
sample = (((tone_phase & 0xFF000000) >> 24) & 0x80) ? 127 : -128;
|
||||
} else if (tone_shape == 6) {
|
||||
} else if (tone_shape == 5) {
|
||||
// Noise generator, pseudo random noise generator, 16 bits linear-feedback shift register (LFSR) algorithm, variant Fibonacci.
|
||||
// https://en.wikipedia.org/wiki/Linear-feedback_shift_register
|
||||
// 16 bits LFSR .taps: 16, 15, 13, 4 ;feedback polynomial: x^16 + x^15 + x^13 + x^4 + 1
|
||||
@@ -79,49 +118,13 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (counter == 15) {
|
||||
counter = 0;
|
||||
}
|
||||
} else if (tone_shape == 7) {
|
||||
// Digital BPSK consecutive 0,1,0,...continuous cycle, 1 bit/symbol, at rate of 2 symbols / Freq Tone Periode... without any Pulse shape at the moment .
|
||||
re = (((tone_phase & 0xFF000000) >> 24) & 0x80) ? 127 : -128; // Sending 2 bits by Periode T of the GUI tone, alternative static phasor to 0, -180º , 0º
|
||||
im = 0;
|
||||
} else if (tone_shape == 8) {
|
||||
// Digital QPSK consecutive 00, 01, 10, 11,00, ...continuous cycle ,2 bits/symbol, at rate of 4 symbols / Freq Tone Periode. not random., without any Pulse shape at the moment .
|
||||
|
||||
switch (((tone_phase & 0xFF000000) >> 24)) {
|
||||
case 0 ... 63: // equivalent to 1/4 of total 360º degrees.
|
||||
/* "00" */
|
||||
re = (sine_table_i8[32]); // we are sending symbol-phasor 45º during 1/4 of the total periode
|
||||
im = (sine_table_i8[32 + 64]); // 32 index = rounded (45º/360º * 255 total sin table steps) = 31,875
|
||||
break;
|
||||
|
||||
case 64 ... 127:
|
||||
/* "01" */
|
||||
re = (sine_table_i8[96]); // symbol-phasor 135º
|
||||
im = (sine_table_i8[96 + 64]); // 96 index = 32 + 256/4
|
||||
break;
|
||||
break;
|
||||
|
||||
case 128 ... 191:
|
||||
/* "10" */
|
||||
re = (sine_table_i8[159]); // symbol-phasor 225º
|
||||
im = (sine_table_i8[159 + 64]); // 159 rounded index = 96 + 256/4 = 159.3
|
||||
break;
|
||||
|
||||
case 192 ... 255:
|
||||
/* "11" */
|
||||
re = (sine_table_i8[223]); // symbol-phasor 315º ; 223 rounded index = (315/360) * 255 =223.125
|
||||
im = (sine_table_i8[((223 + 64) & 0xFF)]); // Max index 255, circular periodic conversion.
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tone_shape != 6) { //(all except Pseudo Random White Noise). We are in (1):periodic signals or (2):BPSK/QPSK , in both cases ,we need Tone updated acum sum phases to modulate in FM / or control phasor phase (BPSK & QPSK.)
|
||||
tone_phase += tone_delta; // In periodic signals(Sine/triangle/square) we are using to FM mod. in BPSK-QSPK we are using to calculate each 1/4 of the periode.
|
||||
if (tone_shape != 5) { // All periodic except Pseudo Random White Noise.
|
||||
tone_phase += tone_delta; // In periodic signals we are using phase to generate the tone to be modulated.
|
||||
}
|
||||
|
||||
if (tone_shape < 7) { // All Option shape signals except BPSK(7) & QPSK(8) we are modulating in FM. (Those two has phase shift modulation XPSK , not FM )
|
||||
if (modulation == 1) {
|
||||
// Do FM modulation
|
||||
delta = sample * fm_delta;
|
||||
|
||||
@@ -130,6 +133,21 @@ void SigGenProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
re = (sine_table_i8[(sphase & 0xFF000000) >> 24]); // sin LUT is not dealing with decimals , output range [-128 ,...127]
|
||||
im = (sine_table_i8[(phase & 0xFF000000) >> 24]);
|
||||
|
||||
} else if (modulation == 4) {
|
||||
// Do Double Side Band modulation
|
||||
re = sample;
|
||||
im = 0;
|
||||
|
||||
} else if (modulation == 5) {
|
||||
// Do AM modulation (100% mod index)
|
||||
re = (127 >> 1) + (sample >> 1);
|
||||
im = 0;
|
||||
|
||||
} else if (modulation == 6) {
|
||||
// Do AM modulation (50% mod index)
|
||||
re = 95 + (sample >> 2);
|
||||
im = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +172,8 @@ void SigGenProcessor::on_message(const Message* const msg) {
|
||||
auto_off = false;
|
||||
|
||||
fm_delta = message.bw * (0xFFFFFFULL / 1536000);
|
||||
tone_shape = message.shape;
|
||||
tone_shape = message.shape & 0xF;
|
||||
modulation = (message.shape & 0xF0) >> 4;
|
||||
|
||||
// lfsr = seed_value ; // Finally not used , init lfsr 8 bits.
|
||||
lfsr_16 = seed_value_16; // init lfsr 16 bits.
|
||||
|
||||
@@ -36,7 +36,7 @@ class SigGenProcessor : public BasebandProcessor {
|
||||
bool configured{false};
|
||||
|
||||
uint32_t tone_delta{0}, fm_delta{}, tone_phase{0};
|
||||
uint8_t tone_shape{};
|
||||
uint8_t tone_shape{}, modulation{};
|
||||
uint32_t sample_count{0};
|
||||
bool auto_off{};
|
||||
int32_t phase{0}, sphase{0}, delta{0}; // they may have sign in the pseudo random sample generation.
|
||||
|
||||
@@ -1060,6 +1060,7 @@ DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluste
|
||||
}
|
||||
}
|
||||
/* go to default */
|
||||
__attribute__ ((fallthrough));
|
||||
#endif
|
||||
default:
|
||||
val = 1; /* Internal error */
|
||||
|
||||
@@ -212,7 +212,7 @@ struct data_t {
|
||||
bool updown_frequency_rx_correction;
|
||||
bool updown_frequency_tx_correction;
|
||||
bool lcd_inverted_mode : 1;
|
||||
bool UNUSED_5 : 1;
|
||||
bool encoder_dial_direction : 1; // false = normal, true = reverse
|
||||
bool UNUSED_6 : 1;
|
||||
bool UNUSED_7 : 1;
|
||||
|
||||
@@ -290,7 +290,7 @@ struct data_t {
|
||||
updown_frequency_rx_correction(false),
|
||||
updown_frequency_tx_correction(false),
|
||||
lcd_inverted_mode(false),
|
||||
UNUSED_5(false),
|
||||
encoder_dial_direction(false),
|
||||
UNUSED_6(false),
|
||||
UNUSED_7(false),
|
||||
|
||||
@@ -417,7 +417,8 @@ void defaults() {
|
||||
set_config_splash(true);
|
||||
set_config_disable_external_tcxo(false);
|
||||
set_encoder_dial_sensitivity(DIAL_SENSITIVITY_NORMAL);
|
||||
set_config_speaker_disable(true); // Disable AK4951 speaker by default (in case of OpenSourceSDRLab H2)
|
||||
set_encoder_dial_direction(false); // false = normal, true = reverse
|
||||
set_config_speaker_disable(true); // Disable AK4951 speaker by default (in case of OpenSourceSDRLab H2)
|
||||
set_menu_color(Color::grey());
|
||||
set_ui_hide_numeric_battery(true); // hide the numeric battery by default - no space to display it
|
||||
|
||||
@@ -1080,6 +1081,13 @@ void set_encoder_rate_multiplier(uint8_t v) {
|
||||
data->encoder_rate_multiplier = v;
|
||||
}
|
||||
|
||||
bool encoder_dial_direction() {
|
||||
return data->encoder_dial_direction;
|
||||
}
|
||||
void set_encoder_dial_direction(bool v) {
|
||||
data->encoder_dial_direction = v;
|
||||
}
|
||||
|
||||
// Recovery mode magic value storage
|
||||
static data_t* data_direct_access = reinterpret_cast<data_t*>(memory::map::backup_ram.base());
|
||||
|
||||
@@ -1246,6 +1254,7 @@ bool debug_dump() {
|
||||
pmem_dump_file.write_line("frequency_tx_correction: " + to_string_dec_uint(data->frequency_tx_correction));
|
||||
pmem_dump_file.write_line("encoder_dial_sensitivity: " + to_string_dec_uint(data->encoder_dial_sensitivity));
|
||||
pmem_dump_file.write_line("encoder_rate_multiplier: " + to_string_dec_uint(data->encoder_rate_multiplier));
|
||||
pmem_dump_file.write_line("encoder_dial_direction: " + to_string_dec_uint(data->encoder_dial_direction)); // 0 = normal, 1 = reverse
|
||||
pmem_dump_file.write_line("headphone_volume_cb: " + to_string_dec_int(data->headphone_volume_cb));
|
||||
pmem_dump_file.write_line("config_mode_storage: 0x" + to_string_hex(data->config_mode_storage, 8));
|
||||
pmem_dump_file.write_line("dst_config: 0x" + to_string_hex((uint32_t)data->dst_config.v, 8));
|
||||
|
||||
@@ -252,6 +252,8 @@ uint8_t encoder_dial_sensitivity();
|
||||
void set_encoder_dial_sensitivity(uint8_t v);
|
||||
uint8_t encoder_rate_multiplier();
|
||||
void set_encoder_rate_multiplier(uint8_t v);
|
||||
bool encoder_dial_direction();
|
||||
void set_encoder_dial_direction(bool v);
|
||||
|
||||
uint32_t config_mode_storage_direct();
|
||||
void set_config_mode_storage_direct(uint32_t v);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(standalone_apps)
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
__pycache__/
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
for fname in $(ls ../graphics/); do
|
||||
fname=$(echo "$fname" | awk -F'.' '{print $1}')
|
||||
out=$(grep -r --exclude="bitmap.hpp" "$fname" ../* )
|
||||
ret=$?
|
||||
if [[ "$ret" -ne 0 ]]; then
|
||||
echo "$fname not found"
|
||||
fi
|
||||
done
|
||||
|
||||
@@ -0,0 +1,265 @@
|
||||
# Copyleft Whiterose from the Dark Army
|
||||
#
|
||||
# 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.
|
||||
|
||||
# this tool allow you flash pp and copy the apps in fastest way. note that you need to have a sdcard switcher to switch sdcard to pc.
|
||||
# although, if you don't have one, you still can take out the sdcard and plug it into your pc, but i think it would not pretty fast.
|
||||
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import sys
|
||||
import glob
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
import os
|
||||
import shutil
|
||||
import time
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
# config
|
||||
SUDO_PASSWORD = ""
|
||||
SDCARD_LABEL = "portapack"
|
||||
|
||||
|
||||
def write_command(ser, command, wait_response=None, remove_echo=False, dont_wait_ch=False):
|
||||
|
||||
# clean buffer
|
||||
while True:
|
||||
try:
|
||||
if ser.in_waiting:
|
||||
ser.read(ser.in_waiting)
|
||||
else:
|
||||
break
|
||||
except:
|
||||
break
|
||||
|
||||
command_with_newline = command + "\r\n"
|
||||
try:
|
||||
ser.write(command_with_newline.encode())
|
||||
except Exception as e:
|
||||
return f"write err: {str(e)}"
|
||||
|
||||
response = ""
|
||||
expected_echo = f"{command}\r\n"
|
||||
wait_response_met = wait_response is None
|
||||
|
||||
while True:
|
||||
try:
|
||||
if ser.in_waiting:
|
||||
msg = ser.read(ser.in_waiting).decode('utf-8')
|
||||
response += msg
|
||||
|
||||
if wait_response and wait_response in response:
|
||||
wait_response_met = True
|
||||
|
||||
if wait_response_met and (dont_wait_ch or "ch> " in response):
|
||||
if remove_echo:
|
||||
return response.replace(expected_echo, "")
|
||||
return response
|
||||
|
||||
except serial.SerialTimeoutException:
|
||||
continue
|
||||
except Exception as e:
|
||||
return f"e: {str(e)}"
|
||||
|
||||
def get_serial_devices():
|
||||
ports = serial.tools.list_ports.comports()
|
||||
|
||||
if not ports:
|
||||
print("no serial device found")
|
||||
return None
|
||||
|
||||
portapack_device = None
|
||||
for port in ports:
|
||||
if port.product == "PortaPack Mayhem":
|
||||
print(f"found your pp:")
|
||||
print(f"device: {port.device}")
|
||||
print(f"description: {port.description}")
|
||||
print(f"hwid: {port.hwid}")
|
||||
if port.manufacturer:
|
||||
print(f"manufacturer: {port.manufacturer}")
|
||||
if port.serial_number:
|
||||
print(f"serial_number: {port.serial_number}")
|
||||
portapack_device = port
|
||||
break
|
||||
|
||||
if not portapack_device:
|
||||
print("no pp device found")
|
||||
|
||||
return portapack_device
|
||||
|
||||
def run_sudo_command(cmd):
|
||||
|
||||
if not SUDO_PASSWORD:
|
||||
print("sudo password not set, please hard code it in the script")
|
||||
return
|
||||
|
||||
try:
|
||||
process = subprocess.Popen(
|
||||
['sudo', '-S'] + cmd,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
universal_newlines=True
|
||||
)
|
||||
stdout, stderr = process.communicate(input=SUDO_PASSWORD + '\n')
|
||||
if process.returncode != 0:
|
||||
raise subprocess.CalledProcessError(process.returncode, cmd, stdout, stderr)
|
||||
return stdout
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"sudo command failed: {e}")
|
||||
raise
|
||||
|
||||
def get_pp_device_linux():
|
||||
try:
|
||||
cmd = ["lsblk", "-o", "NAME,LABEL,FSTYPE,SIZE,MOUNTPOINT", "-n", "-p"]
|
||||
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||
|
||||
# find your pp sd
|
||||
for line in result.stdout.splitlines():
|
||||
clean_line = line.replace('└─', '').replace('├─', '').strip()
|
||||
parts = clean_line.split()
|
||||
|
||||
if len(parts) >= 2 and SDCARD_LABEL in parts[1]: # checker
|
||||
device_path = parts[0]
|
||||
# if path valid
|
||||
if not os.path.exists(device_path):
|
||||
continue
|
||||
|
||||
# if mounted, return mountpoint
|
||||
if len(parts) >= 5:
|
||||
mountpoint = parts[4]
|
||||
return device_path, mountpoint
|
||||
|
||||
# if not, create temp dir and mount
|
||||
mount_point = tempfile.mkdtemp()
|
||||
print(f"mount {device_path} to {mount_point}")
|
||||
run_sudo_command(["mount", "-o", f"uid={os.getuid()},gid={os.getgid()}", device_path, mount_point])
|
||||
return device_path, mount_point
|
||||
|
||||
return None, None
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"command failed: {str(e)}")
|
||||
return None, None
|
||||
|
||||
def wait_for_pp_drive():
|
||||
while True:
|
||||
if os.name == 'nt': # win didn't test yet, TODO
|
||||
drives = [d + ':' for d in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' if os.path.exists(d + ':')]
|
||||
for drive in drives:
|
||||
if os.path.basename(os.path.abspath(drive)).lower() == SDCARD_LABEL:
|
||||
return drive, None
|
||||
else: # linux and mac
|
||||
device_path, mount_point = get_pp_device_linux()
|
||||
if device_path and mount_point:
|
||||
return mount_point, device_path
|
||||
time.sleep(1)
|
||||
|
||||
def run():
|
||||
# worker 1: check device connected
|
||||
device = get_serial_devices()
|
||||
if not device:
|
||||
print("no pp device found")
|
||||
return
|
||||
|
||||
try:
|
||||
# worker 2: open serial and send hackrf cmd
|
||||
ser = serial.Serial(device.device, baudrate=115200, timeout=1)
|
||||
for i in range(2):
|
||||
write_command(ser, "hackrf")
|
||||
ser.close()
|
||||
|
||||
# worker 3: hint user to swtich sdcard to pc
|
||||
print("\033[92m\n\t+------------------------+")
|
||||
print("\t| switch PP sdcard to PC |")
|
||||
print("\t+------------------------+\033[0m")
|
||||
print("wait PP sdcard...")
|
||||
|
||||
# worker 4: wait pp sd
|
||||
mount_point, device_path = wait_for_pp_drive()
|
||||
if not mount_point:
|
||||
print("no pp sd found")
|
||||
return
|
||||
print(f"find pp sd: {mount_point}")
|
||||
|
||||
# worker 5: copy ppma and ppmp apps
|
||||
apps_dir = os.path.join(mount_point, 'APPS')
|
||||
if not os.path.exists(apps_dir):
|
||||
os.makedirs(apps_dir)
|
||||
|
||||
firmware_dir = os.path.join('.', 'firmware', 'application')
|
||||
for ext in ['ppma', 'ppmp']:
|
||||
for file in glob.glob(os.path.join(firmware_dir, f'*.{ext}')):
|
||||
dest = os.path.join(apps_dir, os.path.basename(file))
|
||||
print(f"cp: {os.path.basename(file)}")
|
||||
shutil.copy2(file, dest)
|
||||
|
||||
print("apps copy done")
|
||||
|
||||
# worker 6: umount pp sd
|
||||
if os.name != 'nt' and device_path:
|
||||
run_sudo_command(["umount", mount_point])
|
||||
os.rmdir(mount_point) # 删除临时挂载点
|
||||
|
||||
# worker 7: run flash cmd with hackrf_spiflash util
|
||||
print("\nflash...")
|
||||
process = subprocess.Popen(
|
||||
["hackrf_spiflash", "-w", "./firmware/portapack-mayhem-firmware.bin"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True
|
||||
)
|
||||
|
||||
while True:
|
||||
output = process.stdout.readline()
|
||||
if output:
|
||||
print(output.strip())
|
||||
if process.poll() is not None:
|
||||
break
|
||||
|
||||
if process.returncode != 0:
|
||||
raise subprocess.CalledProcessError(process.returncode, "hackrf_spiflash")
|
||||
|
||||
# worker 8: hint user to swtich sdcard switcher back to pp
|
||||
print("\033[92m\n\t+------------------------------------------------+")
|
||||
print("\t| switch sdcard switcher back to pp, then reboot |")
|
||||
print("\t+------------------------------------------------+\033[0m")
|
||||
|
||||
except serial.SerialException as e:
|
||||
print(f"serial comm error: {str(e)}")
|
||||
except OSError as e:
|
||||
print(f"file operation error: {str(e)}")
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"system command error: {str(e)}")
|
||||
except Exception as e:
|
||||
print(f"error: {str(e)}")
|
||||
# ensure umount when error
|
||||
if os.name != 'nt' and 'mount_point' in locals() and device_path:
|
||||
try:
|
||||
run_sudo_command(["umount", mount_point])
|
||||
os.rmdir(mount_point)
|
||||
except:
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
run()
|
||||
except Exception as e:
|
||||
print(f"error: {str(e)}")
|
||||
sys.exit(1)
|
||||
@@ -53,7 +53,7 @@ AboutView::AboutView(NavigationView& nav) {
|
||||
button_ok.focus();
|
||||
};
|
||||
|
||||
for (auto& authors_line : authors_list) {
|
||||
for (auto& authors_line : mayhem_information_list) {
|
||||
// if it's starting with #, it's a title and we have to substract the '#' and paint yellow
|
||||
if (authors_line.size() > 0) {
|
||||
if (authors_line[0] == '#') {
|
||||
@@ -84,15 +84,13 @@ void AboutView::on_frame_sync() {
|
||||
menu_view.set_highlighted(current + 1);
|
||||
} else {
|
||||
menu_view.set_highlighted(0);
|
||||
// ^ to go back to the REAL top instead of make the 2 at the top to make the title disappeares
|
||||
menu_view.set_highlighted(2); // the first line that has human name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AboutView::focus() {
|
||||
button_ok.focus();
|
||||
menu_view.set_highlighted(2); // the first line that has human name
|
||||
menu_view.set_highlighted(3); // contributors block starting line
|
||||
}
|
||||
|
||||
bool AboutView::on_touch(const TouchEvent) {
|
||||
@@ -141,8 +139,12 @@ def get_contributors(url):
|
||||
|
||||
def generate_content(projects):
|
||||
project_contrib = []
|
||||
project_contrib.append("string_view authors_list[] = {\n")
|
||||
project_contrib.append(" \"# * List of contributors * \",\n")
|
||||
project_contrib.append("string_view mayhem_information_list[] = {\n")
|
||||
project_contrib.append(" \"#****** Mayhem Community ******\",\n")
|
||||
project_contrib.append(" \" \",\n")
|
||||
project_contrib.append(" \" https://discord.mayhem.app\",\n")
|
||||
project_contrib.append(" \" \",\n")
|
||||
project_contrib.append(" \"#**** List of contributors ****\",\n")
|
||||
for project in projects:
|
||||
project_contrib.append(" \" \",\n")
|
||||
project_contrib.append(f" \"#{project[0]}:\",\n")
|
||||
@@ -170,7 +172,7 @@ def pp_create_ui_about_simple_cpp(cpp_file, cppheader, cppcontent, cppfooter):
|
||||
|
||||
def pp_change_ui_about_simple_cpp(cpp_file, cppcontent):
|
||||
content = []
|
||||
content_pattern = re.compile(r"string_view authors_list\[\] = {\n(?:\s+(?:.*,\n)+\s+.*};\n)", re.MULTILINE)
|
||||
content_pattern = re.compile(r"string_view mayhem_information_list\[\] = {\n(?:\s+(?:.*,\n)+\s+.*};\n)", re.MULTILINE)
|
||||
|
||||
# Read original file
|
||||
with open(cpp_file, 'r') as file:
|
||||
|
||||
@@ -161,3 +161,23 @@ write_image(spi_image, output_path)
|
||||
|
||||
percent_remaining = round(1000 * pad_size / spi_size) / 10;
|
||||
print("Space remaining in flash ROM:", pad_size, "bytes (", percent_remaining, "%)")
|
||||
|
||||
|
||||
# copy the fast flash script
|
||||
build_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), 'build')
|
||||
flash_py_path = os.path.join(build_dir, 'flash.py')
|
||||
|
||||
if not os.path.exists(flash_py_path):
|
||||
|
||||
current_dir = os.path.dirname(__file__)
|
||||
source_file = os.path.join(current_dir, 'fast_flash_pp_and_copy_apps.py')
|
||||
|
||||
if not os.path.exists(build_dir):
|
||||
os.makedirs(build_dir)
|
||||
|
||||
# cp
|
||||
import shutil
|
||||
print(f"\ncopy {source_file} to {flash_py_path}\n")
|
||||
shutil.copy2(source_file, flash_py_path)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
Filetype: Flipper SubGhz RAW File
|
||||
Version: 1
|
||||
Frequency: 433920000
|
||||
Preset: FuriHalSubGhzPresetOok650Async
|
||||
Protocol: RAW
|
||||
|
||||
RAW_Data: 210 650 210 650 210 650 630 650 210 650 210 650 210 650 630 650
|
||||
RAW_Data: 210 650 210 650 210 650 630 650 210 650 210 650 210 650 210 1950
|
||||
RAW_Data: 210 650 210 650 210 650 630 650 210 650 210 650 210 650 630 650
|
||||
RAW_Data: 210 650 210 650 210 650 630 650 210 650 210 650 210 650 210 1950
|
||||
Reference in New Issue
Block a user