mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-08-08 17:03:49 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3dfb23481f | |||
| faa4affba1 | |||
| 099afda51c | |||
| 5c09efe8f6 | |||
| 157f08bc49 | |||
| d5e6d9d662 | |||
| 64bdd3e018 | |||
| 928bce083a | |||
| 8859c3d80c | |||
| 1e9406d091 | |||
| 63f6a885d8 | |||
| 608c8c3597 | |||
| 6aee1c9c30 | |||
| 2fa6df596a | |||
| aeefecf5c6 | |||
| 06d4916144 | |||
| 911f0c8881 | |||
| 01cfd360fe | |||
| 13ce56f7bf | |||
| 210ec9dd1b | |||
| 47c5c1e9e5 | |||
| 94fcab19d9 | |||
| 7ca322fed4 | |||
| 3e1d726064 | |||
| 01ba7a57ea | |||
| 699504a703 | |||
| 48ccc1e988 | |||
| 95f7eda9c5 | |||
| af3130faa8 | |||
| 3419ece682 | |||
| b22448de75 | |||
| 66a841e079 | |||
| b15b781039 | |||
| f95467d71b | |||
| 200c57f990 | |||
| d2c66f4515 | |||
| dbdb0bd379 |
@@ -1,3 +1,5 @@
|
||||
> ⚠ **BEFORE BUYING YOURS:** Beware of hardware mods that use a custom firmware, which are [NOT COMPATIBLE](https://github.com/eried/portapack-mayhem/issues/264) with older forks, and they do not follow the license. **ASK THE SELLER if the unit works with an specific firmware, for example, pointing to the [](https://github.com/eried/portapack-mayhem/releases/latest) on this repository**
|
||||
|
||||
# PortaPack Mayhem
|
||||
|
||||
[](https://travis-ci.com/eried/portapack-mayhem) [](https://app.buddy.works/eried/portapack/pipelines/pipeline/252276) [](https://codescene.io/projects/8381) [](https://github.com/eried/portapack-mayhem/releases) [](https://github.com/eried/portapack-mayhem/releases/latest) [](https://hub.docker.com/r/eried/portapack) [](https://discord.gg/tuwVMv3) [](https://www.bountysource.com/teams/portapack-mayhem/issues)
|
||||
@@ -39,7 +41,7 @@ This fork (**Mayhem**) uses *major.minor.release* [semantic versioning](https://
|
||||
## What about Havoc/GridRF/jamesshao8/jboone's?
|
||||
* jboone's PortaPack: the [vanilla](https://en.wikipedia.org/wiki/Vanilla_software) experience
|
||||
* Havoc: It was the most popular fork of jboone's PortaPack, currrently, it is not being maintained nor updated
|
||||
* jamesshao8: He keeps his own version of the fork, while not attached as a fork to anything
|
||||
* jamesshao8: He keeps his own version of the fork, while not attached as a fork to anything. Latest functions do not follow the license and are not being published with source code, so keep this in mind
|
||||
* GridRF: They sell PortaPack clones with their own firmware based on a old version, which has no sourcecode available
|
||||
|
||||
## How can I collaborate
|
||||
|
||||
@@ -231,6 +231,7 @@ set(CPPSRC
|
||||
apps/ui_keyfob.cpp
|
||||
apps/ui_lcr.cpp
|
||||
apps/lge_app.cpp
|
||||
apps/ui_looking_glass_app.cpp
|
||||
apps/ui_mictx.cpp
|
||||
apps/ui_modemsetup.cpp
|
||||
apps/ui_morse.cpp
|
||||
|
||||
@@ -82,6 +82,7 @@ private:
|
||||
6,
|
||||
SymField::SYMFIELD_ALPHANUM
|
||||
};
|
||||
|
||||
NumberField num_ssid_dest {
|
||||
{ 19 * 8, 2 * 16 },
|
||||
2,
|
||||
|
||||
@@ -274,6 +274,7 @@ void JammerView::start_tx() {
|
||||
transmitter_model.enable();
|
||||
|
||||
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
|
||||
mscounter = 0; //euquiq: Reset internal ms counter for do_timer()
|
||||
} else {
|
||||
if (out_of_ranges)
|
||||
nav_.display_modal("Error", "Jamming bandwidth too large.\nMust be less than 24MHz.");
|
||||
@@ -289,6 +290,59 @@ void JammerView::stop_tx() {
|
||||
radio::disable();
|
||||
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
|
||||
jamming = false;
|
||||
cooling = false;
|
||||
}
|
||||
|
||||
//called each 1/60th of second
|
||||
void JammerView::on_timer() {
|
||||
if (++mscounter == 60) {
|
||||
mscounter = 0;
|
||||
if (jamming)
|
||||
{
|
||||
if (cooling)
|
||||
{
|
||||
if (++seconds >= field_timepause.value())
|
||||
{ //Re-start TX
|
||||
transmitter_model.enable();
|
||||
button_transmit.set_text("STOP");
|
||||
baseband::set_jammer(true, (JammerType)options_type.selected_index(), options_speed.selected_index_value());
|
||||
|
||||
int32_t jitter_amount = field_jitter.value();
|
||||
if (jitter_amount)
|
||||
{
|
||||
lfsr_v = lfsr_iterate(lfsr_v);
|
||||
jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount);
|
||||
mscounter += jitter_amount;
|
||||
}
|
||||
|
||||
cooling = false;
|
||||
seconds = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (++seconds >= field_timetx.value()) //Start cooling period:
|
||||
{
|
||||
transmitter_model.disable();
|
||||
button_transmit.set_text("PAUSED");
|
||||
baseband::set_jammer(false, JammerType::TYPE_FSK, 0);
|
||||
|
||||
int32_t jitter_amount = field_jitter.value();
|
||||
if (jitter_amount)
|
||||
{
|
||||
lfsr_v = lfsr_iterate(lfsr_v);
|
||||
jitter_amount = (jitter_amount / 2) - (lfsr_v & jitter_amount);
|
||||
mscounter += jitter_amount;
|
||||
}
|
||||
|
||||
cooling = true;
|
||||
seconds = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
JammerView::JammerView(
|
||||
@@ -309,6 +363,9 @@ JammerView::JammerView(
|
||||
&text_range_total,
|
||||
&options_speed,
|
||||
&options_hop,
|
||||
&field_timetx,
|
||||
&field_timepause,
|
||||
&field_jitter,
|
||||
&button_transmit
|
||||
});
|
||||
|
||||
@@ -321,8 +378,11 @@ JammerView::JammerView(
|
||||
options_hop.set_selected_index(1); // 50ms
|
||||
button_transmit.set_style(&style_val);
|
||||
|
||||
field_timetx.set_value(30);
|
||||
field_timepause.set_value(1);
|
||||
|
||||
button_transmit.on_select = [this](Button&) {
|
||||
if (jamming)
|
||||
if (jamming || cooling)
|
||||
stop_tx();
|
||||
else
|
||||
start_tx();
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "transmitter_model.hpp"
|
||||
#include "message.hpp"
|
||||
#include "jammer.hpp"
|
||||
#include "lfsr_random.hpp"
|
||||
|
||||
using namespace jammer;
|
||||
|
||||
@@ -57,78 +58,11 @@ private:
|
||||
.foreground = Color::grey(),
|
||||
};
|
||||
|
||||
/*static constexpr jammer_range_t range_presets[] = {
|
||||
// GSM900 Orange
|
||||
{ true, 935000000, 945000000 }, // BW:10M
|
||||
// GSM1800 Orange
|
||||
{ true, 1808000000, 1832000000 }, // BW:24M
|
||||
|
||||
// GSM900 SFR
|
||||
{ true, 950000000, 960000000 }, // BW:10M
|
||||
// GSM1800 SFR
|
||||
{ true, 1832000000, 1853000000 }, // BW:21M
|
||||
|
||||
// GSM900 Bouygues
|
||||
{ true, 925000000, 935000000 }, // BW:10M
|
||||
// GSM1800 Bouygues
|
||||
{ true, 1858000000, 1880000000 }, // BW:22M
|
||||
|
||||
// GSM900 Free
|
||||
{ true, 945000000, 950000000 }, // BW:5M
|
||||
|
||||
// GSM-R
|
||||
{ true, 921000000, 925000000 }, // BW:4M
|
||||
|
||||
// DECT
|
||||
{ true, 1880000000, 1900000000 }, // BW: 20MHz
|
||||
|
||||
// PMV AFSK
|
||||
{ true, 162930000, 162970000 }, // BW: 40kHz
|
||||
|
||||
// ISM 433
|
||||
{ true, 433050000, 434790000 }, // Center: 433.92MHz BW: 0.2%
|
||||
|
||||
// ISM 868
|
||||
{ true, 868000000, 868200000 }, // Center: 868.2MHz BW: 40kHz
|
||||
|
||||
// GPS L1
|
||||
{ true, 1575420000 - 500000, 1575420000 + 500000 }, // BW: 1MHz
|
||||
// GPS L2
|
||||
{ true, 1227600000 - 1000000, 1227600000 + 1000000 }, // BW: 2MHz
|
||||
|
||||
// WLAN 2.4G CH1
|
||||
{ true, 2412000000 - 11000000, 2412000000 + 11000000}, // BW: 22MHz
|
||||
// WLAN 2.4G CH2
|
||||
{ true, 2417000000 - 11000000, 2417000000 + 11000000}, // BW: 22MHz
|
||||
// WLAN 2.4G CH3
|
||||
{ true, 2422000000 - 11000000, 2422000000 + 11000000}, // BW: 22MHz
|
||||
// WLAN 2.4G CH4
|
||||
{ true, 2427000000 - 11000000, 2427000000 + 11000000}, // BW: 22MHz
|
||||
// WLAN 2.4G CH5
|
||||
{ true, 2432000000 - 11000000, 2432000000 + 11000000}, // BW: 22MHz
|
||||
// WLAN 2.4G CH6
|
||||
{ true, 2437000000 - 11000000, 2437000000 + 11000000}, // BW: 22MHz
|
||||
// WLAN 2.4G CH7
|
||||
{ true, 2442000000 - 11000000, 2442000000 + 11000000}, // BW: 22MHz
|
||||
// WLAN 2.4G CH8
|
||||
{ true, 2447000000 - 11000000, 2447000000 + 11000000}, // BW: 22MHz
|
||||
// WLAN 2.4G CH9
|
||||
{ true, 2452000000 - 11000000, 2452000000 + 11000000}, // BW: 22MHz
|
||||
// WLAN 2.4G CH10
|
||||
{ true, 2457000000 - 11000000, 2457000000 + 11000000}, // BW: 22MHz
|
||||
// WLAN 2.4G CH11
|
||||
{ true, 2462000000 - 11000000, 2462000000 + 11000000}, // BW: 22MHz
|
||||
// WLAN 2.4G CH12
|
||||
{ true, 2467000000 - 11000000, 2467000000 + 11000000}, // BW: 22MHz
|
||||
// WLAN 2.4G CH13
|
||||
{ true, 2472000000 - 11000000, 2472000000 + 11000000}, // BW: 22MHz
|
||||
};*/
|
||||
|
||||
Labels labels {
|
||||
{ { 2 * 8, 9 * 8 + 4 }, "Start", Color::light_grey() },
|
||||
{ { 23 * 8, 9 * 8 + 4 }, "Stop", Color::light_grey() },
|
||||
{ { 12 * 8, 6 * 8 }, "Center", Color::light_grey() },
|
||||
{ { 12 * 8 + 4, 14 * 8 }, "Width", Color::light_grey() }
|
||||
{ { 2 * 8, 8 * 8 + 4 }, "Start", Color::light_grey() },
|
||||
{ { 23 * 8, 8 * 8 + 4 }, "Stop", Color::light_grey() },
|
||||
{ { 12 * 8, 5 * 8 - 4}, "Center", Color::light_grey() },
|
||||
{ { 12 * 8 + 4, 13 * 8 }, "Width", Color::light_grey() }
|
||||
};
|
||||
|
||||
Checkbox check_enabled {
|
||||
@@ -143,19 +77,19 @@ private:
|
||||
};
|
||||
|
||||
Button button_start {
|
||||
{ 0 * 8, 6 * 16, 11 * 8, 28 },
|
||||
{ 0 * 8, 11 * 8, 11 * 8, 28 },
|
||||
""
|
||||
};
|
||||
Button button_stop {
|
||||
{ 19 * 8, 6 * 16, 11 * 8, 28 },
|
||||
{ 19 * 8, 11 * 8, 11 * 8, 28 },
|
||||
""
|
||||
};
|
||||
Button button_center {
|
||||
{ 76, 4 * 16, 11 * 8, 28 },
|
||||
{ 76, 4 * 15 - 4, 11 * 8, 28 },
|
||||
""
|
||||
};
|
||||
Button button_width {
|
||||
{ 76, 8 * 16, 11 * 8, 28 },
|
||||
{ 76, 8 * 15, 11 * 8, 28 },
|
||||
""
|
||||
};
|
||||
};
|
||||
@@ -178,12 +112,17 @@ private:
|
||||
NavigationView& nav_;
|
||||
|
||||
void start_tx();
|
||||
void on_timer();
|
||||
void stop_tx();
|
||||
void set_jammer_channel(uint32_t i, uint32_t width, uint64_t center, uint32_t duration);
|
||||
void on_retune(const rf::Frequency freq, const uint32_t range);
|
||||
|
||||
JammerChannel * jammer_channels = (JammerChannel*)shared_memory.bb_data.data;
|
||||
bool jamming { false };
|
||||
bool cooling { false }; //euquiq: Indicates jammer in cooldown
|
||||
uint16_t seconds = 0; //euquiq: seconds counter for toggling tx / cooldown
|
||||
int16_t mscounter = 0; //euquiq: Internal ms counter for do_timer()
|
||||
lfsr_word_t lfsr_v = 1; //euquiq: Used to generate "random" Jitter
|
||||
|
||||
static constexpr Style style_val {
|
||||
.font = font::fixed_8x16,
|
||||
@@ -209,13 +148,19 @@ private:
|
||||
};
|
||||
|
||||
Labels labels {
|
||||
{ { 3 * 8, 12 * 16 }, "Type:", Color::light_grey() },
|
||||
{ { 2 * 8, 13 * 16 }, "Speed:", Color::light_grey() },
|
||||
{ { 5 * 8, 14 * 16 }, "Hop:", Color::light_grey() }
|
||||
{ { 2 * 8, 23 * 8 }, "Type:", Color::light_grey() },
|
||||
{ { 1 * 8, 25 * 8 }, "Speed:", Color::light_grey() },
|
||||
{ { 3 * 8, 27 * 8 }, "Hop:", Color::light_grey() },
|
||||
{ { 4 * 8, 29 * 8 }, "TX:", Color::light_grey() },
|
||||
{ { 1 * 8, 31 * 8 }, "Sle3p:", Color::light_grey() }, //euquiq: Token of appreciation to TheSle3p, which made this ehnancement a reality with his bounty.
|
||||
{ { 0 * 8, 33 * 8 }, "Jitter:", Color::light_grey() }, //Maybe the repository curator can keep the "mystype" for some versions.
|
||||
{ { 11 * 8, 29 * 8 }, "Secs.", Color::light_grey() },
|
||||
{ { 11 * 8, 31 * 8 }, "Secs.", Color::light_grey() },
|
||||
{ { 11 * 8, 33 * 8 }, "/60", Color::light_grey() }
|
||||
};
|
||||
|
||||
OptionsField options_type {
|
||||
{ 9 * 8, 12 * 16 },
|
||||
{ 7 * 8, 23 * 8 },
|
||||
8,
|
||||
{
|
||||
{ "Rand FSK", 0 },
|
||||
@@ -226,16 +171,16 @@ private:
|
||||
};
|
||||
|
||||
Text text_range_number {
|
||||
{ 22 * 8, 12 * 16, 2 * 8, 16 },
|
||||
{ 16 * 8, 23 * 8, 2 * 8, 16 },
|
||||
"--"
|
||||
};
|
||||
Text text_range_total {
|
||||
{ 24 * 8, 12 * 16, 3 * 8, 16 },
|
||||
{ 18 * 8, 23 * 8, 3 * 8, 16 },
|
||||
"/--"
|
||||
};
|
||||
|
||||
OptionsField options_speed {
|
||||
{ 9 * 8, 13 * 16 },
|
||||
{ 7 * 8, 25 * 8 },
|
||||
6,
|
||||
{
|
||||
{ "10Hz ", 10 },
|
||||
@@ -247,7 +192,7 @@ private:
|
||||
};
|
||||
|
||||
OptionsField options_hop {
|
||||
{ 9 * 8, 14 * 16 },
|
||||
{ 7 * 8, 27 * 8 },
|
||||
5,
|
||||
{
|
||||
{ "10ms ", 1 },
|
||||
@@ -259,9 +204,33 @@ private:
|
||||
{ "10s ", 1000 }
|
||||
}
|
||||
};
|
||||
|
||||
NumberField field_timetx {
|
||||
{ 7 * 8, 29 * 8 },
|
||||
3,
|
||||
{ 1, 180 },
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
NumberField field_timepause {
|
||||
{ 8 * 8, 31 * 8 },
|
||||
2,
|
||||
{ 1, 60 },
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
NumberField field_jitter {
|
||||
{ 8 * 8, 33 * 8 },
|
||||
2,
|
||||
{ 1, 60 },
|
||||
1,
|
||||
' ',
|
||||
};
|
||||
|
||||
Button button_transmit {
|
||||
{ 9 * 8, 16 * 16, 96, 48 },
|
||||
{ 148, 212, 80, 80},
|
||||
"START"
|
||||
};
|
||||
|
||||
@@ -272,6 +241,14 @@ private:
|
||||
this->on_retune(message->freq, message->range);
|
||||
}
|
||||
};
|
||||
|
||||
MessageHandlerRegistration message_handler_frame_sync {
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
this->on_timer();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
||||
|
||||
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2020 euquiq
|
||||
*
|
||||
* 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_looking_glass_app.hpp"
|
||||
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui
|
||||
{
|
||||
|
||||
void GlassView::focus() {
|
||||
field_marker.focus();
|
||||
}
|
||||
|
||||
GlassView::~GlassView() {
|
||||
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
void GlassView::on_lna_changed(int32_t v_db) {
|
||||
receiver_model.set_lna(v_db);
|
||||
}
|
||||
|
||||
void GlassView::on_vga_changed(int32_t v_db) {
|
||||
receiver_model.set_vga(v_db);
|
||||
}
|
||||
|
||||
void GlassView::add_spectrum_pixel(Color color)
|
||||
{
|
||||
spectrum_row[pixel_index++] = color;
|
||||
if (pixel_index == 240) //got an entire waterfall line
|
||||
{
|
||||
const auto draw_y = display.scroll(1); //Scroll 1 pixel down
|
||||
display.draw_pixels( {{0, draw_y}, {240, 1}}, spectrum_row); //new line at top
|
||||
pixel_index = 0; //Start New cascade line
|
||||
}
|
||||
}
|
||||
|
||||
//Apparently, the spectrum object returns an array of 256 bins
|
||||
//Each having the radio signal power for it's corresponding frequency slot
|
||||
void GlassView::on_channel_spectrum(const ChannelSpectrum &spectrum)
|
||||
{
|
||||
baseband::spectrum_streaming_stop();
|
||||
|
||||
// Convert bins of this spectrum slice into a representative max_power and when enough, into pixels
|
||||
// Spectrum.db has 256 bins. Center 12 bins are ignored (DC spike is blanked) Leftmost and rightmost 2 bins are ignored
|
||||
// All things said and done, we actually need 240 of those bins:
|
||||
for(uint8_t bin = 0; bin < 240; bin++)
|
||||
{
|
||||
if (bin < 120) {
|
||||
if (spectrum.db[134 + bin] > max_power)
|
||||
max_power = spectrum.db[134 + bin];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spectrum.db[bin - 118] > max_power)
|
||||
max_power = spectrum.db[bin - 118];
|
||||
}
|
||||
|
||||
bins_Hz_size += each_bin_size; //add this bin Hz count into the "pixel fulfilled bag of Hz"
|
||||
|
||||
if (bins_Hz_size >= marker_pixel_step) //new pixel fullfilled
|
||||
{
|
||||
if (min_color_power < max_power)
|
||||
add_spectrum_pixel(spectrum_rgb3_lut[max_power]); //Pixel will represent max_power
|
||||
else
|
||||
add_spectrum_pixel(0); //Filtered out, show black
|
||||
|
||||
max_power = 0;
|
||||
|
||||
if (!pixel_index) //Received indication that a waterfall line has been completed
|
||||
{
|
||||
bins_Hz_size = 0; //Since this is an entire pixel line, we don't carry "Pixels into next bin"
|
||||
break;
|
||||
} else {
|
||||
bins_Hz_size -= marker_pixel_step; //reset bins size, but carrying the eventual excess Hz into next pixel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pixel_index)
|
||||
f_center += SEARCH_SLICE_WIDTH; //Move into the next bandwidth slice NOTE: spectrum.sampling_rate = SEARCH_SLICE_WIDTH
|
||||
else
|
||||
f_center = f_center_ini; //Start a new sweep
|
||||
|
||||
receiver_model.set_tuning_frequency(f_center); //tune rx for this slice
|
||||
baseband::spectrum_streaming_start(); //Do the RX
|
||||
}
|
||||
|
||||
void GlassView::on_hide()
|
||||
{
|
||||
baseband::spectrum_streaming_stop();
|
||||
display.scroll_disable();
|
||||
}
|
||||
|
||||
void GlassView::on_show()
|
||||
{
|
||||
display.scroll_set_area( 109, 319); //Restart scroll on the correct coordinates
|
||||
baseband::spectrum_streaming_start();
|
||||
}
|
||||
|
||||
void GlassView::on_range_changed()
|
||||
{
|
||||
f_min = field_frequency_min.value();
|
||||
f_max = field_frequency_max.value();
|
||||
search_span = f_max - f_min;
|
||||
|
||||
field_marker.set_range(f_min, f_max); //Move the marker between range
|
||||
field_marker.set_value(f_min + (search_span / 2)); //Put MARKER AT MIDDLE RANGE
|
||||
text_range.set(to_string_dec_uint(search_span));
|
||||
|
||||
f_min = (f_min)*MHZ_DIV; //Transpose into full frequency realm
|
||||
f_max = (f_max)*MHZ_DIV;
|
||||
search_span = search_span * MHZ_DIV;
|
||||
|
||||
marker_pixel_step = search_span / 240; //Each pixel value in Hz
|
||||
text_marker_pm.set(to_string_dec_uint((marker_pixel_step / X2_MHZ_DIV) + 1)); // Give idea of +/- marker precision
|
||||
|
||||
int32_t marker_step = marker_pixel_step / MHZ_DIV;
|
||||
if (!marker_step)
|
||||
field_marker.set_step(1); //in case selected range is less than 240 (pixels)
|
||||
else
|
||||
field_marker.set_step(marker_step); //step needs to be a pixel wide.
|
||||
|
||||
f_center_ini = f_min + (SEARCH_SLICE_WIDTH / 2); //Initial center frequency for sweep
|
||||
f_center_ini += SEARCH_SLICE_WIDTH; //euquiq: Why do I need to move the center ???!!! (shift needed for marker accuracy)
|
||||
|
||||
PlotMarker(field_marker.value()); //Refresh marker on screen
|
||||
|
||||
f_center = f_center_ini; //Reset sweep into first slice
|
||||
pixel_index = 0; //reset pixel counter
|
||||
max_power = 0;
|
||||
bins_Hz_size = 0; //reset amount of Hz filled up by pixels
|
||||
|
||||
baseband::set_spectrum(SEARCH_SLICE_WIDTH, field_trigger.value());
|
||||
receiver_model.set_tuning_frequency(f_center_ini); //tune rx for this slice
|
||||
}
|
||||
|
||||
void GlassView::PlotMarker(rf::Frequency pos)
|
||||
{
|
||||
pos = pos * MHZ_DIV;
|
||||
pos -= f_min;
|
||||
pos = pos / marker_pixel_step; //Real pixel
|
||||
|
||||
portapack::display.fill_rectangle({0, 100, 240, 8}, Color::black()); //Clear old marker and whole marker rectangle btw
|
||||
portapack::display.fill_rectangle({pos - 2, 100, 5, 3}, Color::red()); //Red marker middle
|
||||
portapack::display.fill_rectangle({pos - 1, 103, 3, 3}, Color::red()); //Red marker middle
|
||||
portapack::display.fill_rectangle({pos, 106, 1, 2}, Color::red()); //Red marker middle
|
||||
}
|
||||
|
||||
GlassView::GlassView(
|
||||
NavigationView &nav) : nav_(nav)
|
||||
{
|
||||
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
|
||||
|
||||
add_children({&labels,
|
||||
&field_frequency_min,
|
||||
&field_frequency_max,
|
||||
&field_lna,
|
||||
&field_vga,
|
||||
&text_range,
|
||||
&filter_config,
|
||||
&field_rf_amp,
|
||||
&range_presets,
|
||||
&field_marker,
|
||||
&text_marker_pm,
|
||||
&field_trigger
|
||||
});
|
||||
|
||||
load_Presets(); //Load available presets from TXT files (or default)
|
||||
|
||||
field_frequency_min.set_value(presets_db[0].min); //Defaults to first preset
|
||||
field_frequency_min.on_change = [this](int32_t v) {
|
||||
if (v >= field_frequency_max.value())
|
||||
field_frequency_max.set_value(v + 240);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_frequency_max.set_value(presets_db[0].max); //Defaults to first preset
|
||||
field_frequency_max.on_change = [this](int32_t v) {
|
||||
if (v <= field_frequency_min.value())
|
||||
field_frequency_min.set_value(v - 240);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_lna.set_value(receiver_model.lna());
|
||||
field_lna.on_change = [this](int32_t v) {
|
||||
this->on_lna_changed(v);
|
||||
};
|
||||
|
||||
field_vga.set_value(receiver_model.vga());
|
||||
field_vga.on_change = [this](int32_t v_db) {
|
||||
this->on_vga_changed(v_db);
|
||||
};
|
||||
|
||||
filter_config.set_selected_index(0);
|
||||
filter_config.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
min_color_power = v;
|
||||
};
|
||||
|
||||
range_presets.on_change = [this](size_t n, OptionsField::value_t v) {
|
||||
field_frequency_min.set_value(presets_db[v].min,false);
|
||||
field_frequency_max.set_value(presets_db[v].max,false);
|
||||
this->on_range_changed();
|
||||
};
|
||||
|
||||
field_marker.on_change = [this](int32_t v) {
|
||||
PlotMarker(v); //Refresh marker on screen
|
||||
};
|
||||
|
||||
field_marker.on_select = [this](NumberField&) {
|
||||
f_center = field_marker.value();
|
||||
f_center = f_center * MHZ_DIV;
|
||||
receiver_model.set_tuning_frequency(f_center); //Center tune rx in marker freq.
|
||||
receiver_model.set_frequency_step(MHZ_DIV); // Preset a 1 MHz frequency step into RX -> AUDIO
|
||||
nav_.pop();
|
||||
nav_.push<AnalogAudioView>(); //Jump into audio view
|
||||
};
|
||||
|
||||
field_trigger.set_value(32); //Defaults to 32, as normal triggering resolution
|
||||
field_trigger.on_change = [this](int32_t v) {
|
||||
baseband::set_spectrum(SEARCH_SLICE_WIDTH, v);
|
||||
};
|
||||
|
||||
display.scroll_set_area( 109, 319);
|
||||
baseband::set_spectrum(SEARCH_SLICE_WIDTH, field_trigger.value()); //trigger:
|
||||
// Discord User jteich: WidebandSpectrum::on_message to set the trigger value. In WidebandSpectrum::execute ,
|
||||
// it keeps adding the output of the fft to the buffer until "trigger" number of calls are made,
|
||||
//at which time it pushes the buffer up with channel_spectrum.feed
|
||||
|
||||
on_range_changed();
|
||||
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::SpectrumAnalysis);
|
||||
receiver_model.set_sampling_rate(SEARCH_SLICE_WIDTH); //20mhz
|
||||
receiver_model.set_baseband_bandwidth(SEARCH_SLICE_WIDTH); // possible values: 1.75/2.5/3.5/5/5.5/6/7/8/9/10/12/14/15/20/24/28MHz
|
||||
receiver_model.set_squelch_level(0);
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
void GlassView::load_Presets() {
|
||||
File presets_file; //LOAD /WHIPCALC/ANTENNAS.TXT from microSD
|
||||
auto result = presets_file.open("LOOKINGGLASS/PRESETS.TXT");
|
||||
presets_db.clear(); //Start with fresh db
|
||||
if (result.is_valid()) {
|
||||
presets_Default(); //There is no txt, store a default range
|
||||
} else {
|
||||
|
||||
std::string line; //There is a txt file
|
||||
char one_char[1]; //Read it char by char
|
||||
for (size_t pointer=0; pointer < presets_file.size();pointer++) {
|
||||
presets_file.seek(pointer);
|
||||
presets_file.read(one_char, 1);
|
||||
if ((int)one_char[0] > 31) { //ascii space upwards
|
||||
line += one_char[0]; //Add it to the textline
|
||||
}
|
||||
else if (one_char[0] == '\n') { //New Line
|
||||
txtline_process(line); //make sense of this textline
|
||||
line.clear(); //Ready for next textline
|
||||
}
|
||||
}
|
||||
if (line.length() > 0) txtline_process(line); //Last line had no newline at end ?
|
||||
if (!presets_db.size()) presets_Default(); //no antenna on txt, use default
|
||||
}
|
||||
|
||||
populate_Presets();
|
||||
}
|
||||
|
||||
void GlassView::txtline_process(std::string& line)
|
||||
{
|
||||
if (line.find("#") != std::string::npos) return; //Line is just a comment
|
||||
|
||||
size_t comma = line.find(","); //Get first comma position
|
||||
if (comma == std::string::npos) return; //No comma at all
|
||||
|
||||
size_t previous = 0;
|
||||
preset_entry new_preset;
|
||||
|
||||
new_preset.min = std::stoi(line.substr(0,comma));
|
||||
if (!new_preset.min) return; //No frequency!
|
||||
|
||||
previous = comma + 1;
|
||||
comma = line.find(",",previous); //Search for next delimiter
|
||||
if (comma == std::string::npos) return; //No comma at all
|
||||
|
||||
new_preset.max = std::stoi(line.substr(previous,comma - previous));
|
||||
if (!new_preset.max) return; //No frequency!
|
||||
|
||||
new_preset.label = line.substr(comma + 1);
|
||||
if (new_preset.label.size() == 0) return; //No label ?
|
||||
|
||||
presets_db.push_back(new_preset); //Add this preset.
|
||||
}
|
||||
|
||||
void GlassView::populate_Presets()
|
||||
{
|
||||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
options_t entries;
|
||||
|
||||
for (preset_entry preset : presets_db)
|
||||
{ //go thru all available presets
|
||||
entries.emplace_back(preset.label,entries.size());
|
||||
}
|
||||
range_presets.set_options(entries);
|
||||
}
|
||||
|
||||
void GlassView::presets_Default()
|
||||
{
|
||||
presets_db.clear();
|
||||
presets_db.push_back({2320, 2560, "DEFAULT WIFI 2.4GHz"});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2020 euquiq
|
||||
*
|
||||
* 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 "portapack.hpp"
|
||||
#include "baseband_api.hpp"
|
||||
#include "receiver_model.hpp"
|
||||
#include "ui_widget.hpp"
|
||||
#include "ui_navigation.hpp"
|
||||
#include "ui_receiver.hpp"
|
||||
#include "string_format.hpp"
|
||||
#include "analog_audio_app.hpp"
|
||||
#include "spectrum_color_lut.hpp"
|
||||
|
||||
namespace ui
|
||||
{
|
||||
#define SEARCH_SLICE_WIDTH 20000000 // Each slice bandwidth 20 MHz
|
||||
#define MHZ_DIV 1000000
|
||||
#define X2_MHZ_DIV 2000000
|
||||
|
||||
class GlassView : public View
|
||||
{
|
||||
public:
|
||||
GlassView(NavigationView &nav);
|
||||
~GlassView();
|
||||
std::string title() const override { return "Looking Glass"; };
|
||||
|
||||
void on_show() override;
|
||||
void on_hide() override;
|
||||
void focus() override;
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
struct preset_entry
|
||||
{
|
||||
rf::Frequency min{};
|
||||
rf::Frequency max{};
|
||||
std::string label{};
|
||||
};
|
||||
|
||||
std::vector<preset_entry> presets_db{};
|
||||
|
||||
void on_channel_spectrum(const ChannelSpectrum& spectrum);
|
||||
void do_timers();
|
||||
void on_range_changed();
|
||||
void on_lna_changed(int32_t v_db);
|
||||
void on_vga_changed(int32_t v_db);
|
||||
void add_spectrum_pixel(Color color);
|
||||
void PlotMarker(rf::Frequency pos);
|
||||
void load_Presets();
|
||||
void txtline_process(std::string& line);
|
||||
void populate_Presets();
|
||||
void presets_Default();
|
||||
|
||||
rf::Frequency f_min { 0 }, f_max { 0 };
|
||||
rf::Frequency search_span { 0 };
|
||||
rf::Frequency f_center { 0 };
|
||||
rf::Frequency f_center_ini { 0 };
|
||||
rf::Frequency marker_pixel_step { 0 };
|
||||
rf::Frequency each_bin_size { SEARCH_SLICE_WIDTH / 240 };
|
||||
rf::Frequency bins_Hz_size { 0 };
|
||||
uint8_t min_color_power { 0 };
|
||||
uint32_t pixel_index { 0 };
|
||||
std::array<Color, 240> spectrum_row = { 0 };
|
||||
ChannelSpectrumFIFO* fifo { nullptr };
|
||||
uint8_t max_power = 0;
|
||||
|
||||
Labels labels{
|
||||
{{0, 0}, "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 +/- MHz", Color::light_grey()},
|
||||
{{0, 4 * 16}, "RESOLUTION: (fft trigger)", Color::light_grey()}
|
||||
};
|
||||
|
||||
NumberField field_frequency_min {
|
||||
{ 4 * 8, 0 * 16 },
|
||||
4,
|
||||
{ 0, 6960 },
|
||||
240,
|
||||
' '
|
||||
};
|
||||
|
||||
NumberField field_frequency_max {
|
||||
{ 13 * 8, 0 * 16 },
|
||||
4,
|
||||
{ 240, 7200 },
|
||||
240,
|
||||
' '
|
||||
};
|
||||
|
||||
LNAGainField field_lna {
|
||||
{ 21 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
VGAGainField field_vga {
|
||||
{ 27 * 8, 0 * 16 }
|
||||
};
|
||||
|
||||
Text text_range{
|
||||
{7 * 8, 1 * 16, 4 * 8, 16},
|
||||
""};
|
||||
|
||||
OptionsField filter_config{
|
||||
{19 * 8, 1 * 16},
|
||||
4,
|
||||
{
|
||||
{"OFF ", 0},
|
||||
{"MID ", 118}, //85 +25 (110) + a bit more to kill all blue
|
||||
{"HIGH", 202}, //168 + 25 (193)
|
||||
}};
|
||||
|
||||
RFAmpField field_rf_amp{
|
||||
{29 * 8, 1 * 16}};
|
||||
|
||||
OptionsField range_presets{
|
||||
{7 * 8, 2 * 16},
|
||||
20,
|
||||
{
|
||||
{" NONE (WIFI 2.4GHz)", 0},
|
||||
}};
|
||||
|
||||
NumberField field_marker{
|
||||
{7 * 8, 3 * 16},
|
||||
4,
|
||||
{0, 7200},
|
||||
25,
|
||||
' '};
|
||||
|
||||
Text text_marker_pm{
|
||||
{20 * 8, 3 * 16, 2 * 8, 16},
|
||||
""};
|
||||
|
||||
NumberField field_trigger{
|
||||
{11 * 8, 4 * 16},
|
||||
3,
|
||||
{2, 128},
|
||||
2,
|
||||
' '};
|
||||
|
||||
|
||||
MessageHandlerRegistration message_handler_spectrum_config {
|
||||
Message::ID::ChannelSpectrumConfig,
|
||||
[this](const Message* const p) {
|
||||
const auto message = *reinterpret_cast<const ChannelSpectrumConfigMessage*>(p);
|
||||
this->fifo = message.fifo;
|
||||
}
|
||||
};
|
||||
MessageHandlerRegistration message_handler_frame_sync {
|
||||
Message::ID::DisplayFrameSync,
|
||||
[this](const Message* const) {
|
||||
if( this->fifo ) {
|
||||
ChannelSpectrum channel_spectrum;
|
||||
while( fifo->out(channel_spectrum) ) {
|
||||
this->on_channel_spectrum(channel_spectrum);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
#include "ui_scanner.hpp"
|
||||
#include "ui_fileman.hpp"
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
@@ -182,6 +183,7 @@ ScannerView::ScannerView(
|
||||
&field_bw,
|
||||
&field_squelch,
|
||||
&field_wait,
|
||||
&button_load,
|
||||
&rssi,
|
||||
&text_cycle,
|
||||
&text_max,
|
||||
@@ -211,6 +213,25 @@ ScannerView::ScannerView(
|
||||
frequency_range.max = stored_freq + 1000000;
|
||||
button_manual_end.set_text(to_string_short_freq(frequency_range.max));
|
||||
|
||||
button_load.on_select = [this, &nav](Button&) {
|
||||
// load txt files from the FREQMAN folder
|
||||
auto open_view = nav.push<FileLoadView>(".TXT");
|
||||
open_view->on_changed = [this](std::filesystem::path new_file_path) {
|
||||
|
||||
std::string dir_filter = "FREQMAN/";
|
||||
std::string str_file_path = new_file_path.string();
|
||||
|
||||
if (str_file_path.find(dir_filter) != string::npos) { // assert file from the FREQMAN folder
|
||||
scan_pause();
|
||||
// get the filename without txt extension so we can use load_freqman_file fcn
|
||||
std::string str_file_name = new_file_path.stem().string();
|
||||
frequency_file_load(str_file_name, true);
|
||||
} else {
|
||||
nav_.display_modal("LOAD ERROR", "A valid file from\nFREQMAN directory is\nrequired.");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
button_manual_start.on_select = [this, &nav](Button& button) {
|
||||
auto new_view = nav_.push<FrequencyKeypadView>(frequency_range.min);
|
||||
new_view->on_changed = [this, &button](rf::Frequency f) {
|
||||
@@ -314,7 +335,8 @@ ScannerView::ScannerView(
|
||||
|
||||
button_add.on_select = [this](Button&) { //frequency_list[current_index]
|
||||
File scanner_file;
|
||||
auto result = scanner_file.open("FREQMAN/SCANNER.TXT"); //First search if freq is already in txt
|
||||
std::string freq_file_path = "FREQMAN/" + loaded_file_name + ".TXT";
|
||||
auto result = scanner_file.open(freq_file_path); //First search if freq is already in txt
|
||||
if (!result.is_valid()) {
|
||||
std::string frequency_to_add = "f="
|
||||
+ to_string_dec_uint(frequency_list[current_index] / 1000)
|
||||
@@ -342,12 +364,12 @@ ScannerView::ScannerView(
|
||||
big_display.set(frequency_list[current_index]); //After showing an error
|
||||
}
|
||||
else {
|
||||
auto result = scanner_file.append("FREQMAN/SCANNER.TXT"); //Second: append if it is not there
|
||||
auto result = scanner_file.append(freq_file_path); //Second: append if it is not there
|
||||
scanner_file.write_line(frequency_to_add + ",d=ADD FQ");
|
||||
}
|
||||
} else
|
||||
{
|
||||
nav_.display_modal("Error", "Cannot open SCANNER.TXT\nfor appending freq.");
|
||||
nav_.display_modal("Error", "Cannot open " + loaded_file_name + ".TXT\nfor appending freq.");
|
||||
big_display.set(frequency_list[current_index]); //After showing an error
|
||||
}
|
||||
};
|
||||
@@ -357,22 +379,37 @@ ScannerView::ScannerView(
|
||||
field_squelch.on_change = [this](int32_t v) { squelch = v; }; field_squelch.set_value(-10);
|
||||
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
|
||||
field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); };
|
||||
|
||||
// LEARN FREQUENCIES
|
||||
std::string scanner_txt = "SCANNER";
|
||||
if ( load_freqman_file(scanner_txt, database) ) {
|
||||
frequency_file_load(scanner_txt);
|
||||
}
|
||||
|
||||
void ScannerView::frequency_file_load(std::string file_name, bool stop_all_before) {
|
||||
|
||||
// stop everything running now if required
|
||||
if (stop_all_before) {
|
||||
scan_thread->stop();
|
||||
frequency_list.clear(); // clear the existing frequency list (expected behavior)
|
||||
description_list.clear();
|
||||
def_step = step_mode.selected_index_value(); //Use def_step from manual selector
|
||||
}
|
||||
|
||||
if ( load_freqman_file(file_name, database) ) {
|
||||
loaded_file_name = file_name; // keep loaded filename in memory
|
||||
for(auto& entry : database) { // READ LINE PER LINE
|
||||
if (frequency_list.size() < MAX_DB_ENTRY) { //We got space!
|
||||
if (entry.type == RANGE) { //RANGE
|
||||
switch (entry.step) {
|
||||
case AM_US: def_step = 10000; break ;
|
||||
case AM_EUR:def_step = 9000; break ;
|
||||
case NFM_1: def_step = 12500; break ;
|
||||
case NFM_2: def_step = 6250; break ;
|
||||
case FM_1: def_step = 100000; break ;
|
||||
case FM_2: def_step = 50000; break ;
|
||||
case N_1: def_step = 25000; break ;
|
||||
case N_2: def_step = 250000; break ;
|
||||
case AIRBAND:def_step= 8330; break ;
|
||||
case AM_US: def_step = 10000; break ;
|
||||
case AM_EUR:def_step = 9000; break ;
|
||||
case NFM_1: def_step = 12500; break ;
|
||||
case NFM_2: def_step = 6250; break ;
|
||||
case FM_1: def_step = 100000; break ;
|
||||
case FM_2: def_step = 50000; break ;
|
||||
case N_1: def_step = 25000; break ;
|
||||
case N_2: def_step = 250000; break ;
|
||||
case AIRBAND:def_step= 8330; break ;
|
||||
}
|
||||
frequency_list.push_back(entry.frequency_a); //Store starting freq and description
|
||||
description_list.push_back("R" + to_string_short_freq(entry.frequency_a)
|
||||
@@ -398,7 +435,8 @@ ScannerView::ScannerView(
|
||||
}
|
||||
else
|
||||
{
|
||||
desc_cycle.set(" NO SCANNER.TXT FILE ..." );
|
||||
loaded_file_name = 'SCANNER'; // back to the default frequency file
|
||||
desc_cycle.set(" NO " + file_name + ".TXT FILE ..." );
|
||||
}
|
||||
audio::output::stop();
|
||||
step_mode.set_by_value(def_step); //Impose the default step into the manual step selector
|
||||
|
||||
@@ -126,6 +126,7 @@ private:
|
||||
void scan_pause();
|
||||
void scan_resume();
|
||||
void user_resume();
|
||||
void frequency_file_load(std::string file_name, bool stop_all_before = false);
|
||||
|
||||
void on_statistics_update(const ChannelStatistics& statistics);
|
||||
void on_headphone_volume_changed(int32_t v);
|
||||
@@ -137,6 +138,7 @@ private:
|
||||
uint32_t wait { 0 };
|
||||
size_t def_step { 0 };
|
||||
freqman_db database { };
|
||||
std::string loaded_file_name;
|
||||
uint32_t current_index { 0 };
|
||||
bool userpause { false };
|
||||
|
||||
@@ -278,6 +280,11 @@ private:
|
||||
"ADD FQ"
|
||||
};
|
||||
|
||||
Button button_load {
|
||||
{ 24 * 8, 3 * 16 - 8, 6 * 8, 22 },
|
||||
"Load"
|
||||
};
|
||||
|
||||
Button button_remove {
|
||||
{ 168, (35 * 8) - 4, 72, 28 },
|
||||
"DEL FQ"
|
||||
|
||||
@@ -144,6 +144,10 @@ SetRadioView::SetRadioView(
|
||||
}
|
||||
|
||||
add_children({
|
||||
&check_clkout,
|
||||
&field_clkout_freq,
|
||||
&labels_clkout_khz,
|
||||
&value_freq_step,
|
||||
&labels_bias,
|
||||
&check_bias,
|
||||
&button_done,
|
||||
@@ -156,6 +160,39 @@ SetRadioView::SetRadioView(
|
||||
|
||||
form_init(model);
|
||||
|
||||
check_clkout.set_value(portapack::persistent_memory::clkout_enabled());
|
||||
check_clkout.on_select = [this](Checkbox&, bool v) {
|
||||
clock_manager.enable_clock_output(v);
|
||||
portapack::persistent_memory::set_clkout_enabled(v);
|
||||
StatusRefreshMessage message { };
|
||||
EventDispatcher::send_message(message);
|
||||
};
|
||||
|
||||
field_clkout_freq.set_value(portapack::persistent_memory::clkout_freq());
|
||||
value_freq_step.set_style(&style_text);
|
||||
|
||||
field_clkout_freq.on_select = [this](NumberField&) {
|
||||
freq_step_khz++;
|
||||
if(freq_step_khz > 3) {
|
||||
freq_step_khz = 0;
|
||||
}
|
||||
switch(freq_step_khz) {
|
||||
case 0:
|
||||
value_freq_step.set(" |");
|
||||
break;
|
||||
case 1:
|
||||
value_freq_step.set(" | ");
|
||||
break;
|
||||
case 2:
|
||||
value_freq_step.set(" | ");
|
||||
break;
|
||||
case 3:
|
||||
value_freq_step.set("| ");
|
||||
break;
|
||||
}
|
||||
field_clkout_freq.set_step(pow(10, freq_step_khz));
|
||||
};
|
||||
|
||||
check_bias.set_value(portapack::get_antenna_bias());
|
||||
check_bias.on_select = [this](Checkbox&, bool v) {
|
||||
portapack::set_antenna_bias(v);
|
||||
@@ -166,6 +203,8 @@ SetRadioView::SetRadioView(
|
||||
button_done.on_select = [this, &nav](Button&){
|
||||
const auto model = this->form_collect();
|
||||
portapack::persistent_memory::set_correction_ppb(model.ppm * 1000);
|
||||
portapack::persistent_memory::set_clkout_freq(model.freq);
|
||||
clock_manager.enable_clock_output(portapack::persistent_memory::clkout_enabled());
|
||||
nav.pop();
|
||||
};
|
||||
}
|
||||
@@ -181,6 +220,7 @@ void SetRadioView::form_init(const SetFrequencyCorrectionModel& model) {
|
||||
SetFrequencyCorrectionModel SetRadioView::form_collect() {
|
||||
return {
|
||||
.ppm = static_cast<int8_t>(field_ppm.value()),
|
||||
.freq = static_cast<uint32_t>(field_clkout_freq.value()),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ private:
|
||||
|
||||
struct SetFrequencyCorrectionModel {
|
||||
int8_t ppm;
|
||||
uint32_t freq;
|
||||
};
|
||||
|
||||
class SetRadioView : public View {
|
||||
@@ -130,6 +131,7 @@ private:
|
||||
.background = Color::black(),
|
||||
.foreground = Color::light_grey(),
|
||||
};
|
||||
uint8_t freq_step_khz = 3;
|
||||
|
||||
Text label_source {
|
||||
{ 0, 1 * 16, 17 * 8, 16 },
|
||||
@@ -147,8 +149,31 @@ private:
|
||||
};
|
||||
|
||||
Labels labels_correction {
|
||||
{ { 2 * 8, 4 * 16 }, "Frequency correction:", Color::light_grey() },
|
||||
{ { 6 * 8, 5 * 16 }, "PPM", Color::light_grey() },
|
||||
{ { 2 * 8, 3 * 16 }, "Frequency correction:", Color::light_grey() },
|
||||
{ { 6 * 8, 4 * 16 }, "PPM", Color::light_grey() },
|
||||
};
|
||||
|
||||
Checkbox check_clkout {
|
||||
{ 18, (6 * 16 - 4) },
|
||||
13,
|
||||
"Enable CLKOUT"
|
||||
};
|
||||
|
||||
NumberField field_clkout_freq {
|
||||
{ 20 * 8, 6 * 16 },
|
||||
5,
|
||||
{ 10, 60000 },
|
||||
1000,
|
||||
' '
|
||||
};
|
||||
|
||||
Labels labels_clkout_khz {
|
||||
{ { 26 * 8, 6 * 16 }, "kHz", Color::light_grey() }
|
||||
};
|
||||
|
||||
Text value_freq_step {
|
||||
{ 21 * 8, (7 * 16 ), 4 * 8, 16 },
|
||||
"| "
|
||||
};
|
||||
|
||||
Labels labels_bias {
|
||||
@@ -159,7 +184,7 @@ private:
|
||||
};
|
||||
|
||||
NumberField field_ppm {
|
||||
{ 2 * 8, 5 * 16 },
|
||||
{ 2 * 8, 4 * 16 },
|
||||
3,
|
||||
{ -50, 50 },
|
||||
1,
|
||||
|
||||
+1967
-1945
File diff suppressed because it is too large
Load Diff
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "clock_manager.hpp"
|
||||
|
||||
#include "portapack_persistent_memory.hpp"
|
||||
#include "portapack_io.hpp"
|
||||
|
||||
#include "hackrf_hal.hpp"
|
||||
@@ -463,3 +464,21 @@ void ClockManager::stop_audio_pll() {
|
||||
cgu::pll0audio::power_down();
|
||||
while( cgu::pll0audio::is_locked() );
|
||||
}
|
||||
|
||||
void ClockManager::enable_clock_output(bool enable) {
|
||||
if(enable) {
|
||||
clock_generator.enable_output(clock_generator_output_clkout);
|
||||
if(portapack::persistent_memory::clkout_freq() < 1000) {
|
||||
clock_generator.set_ms_frequency(clock_generator_output_clkout, portapack::persistent_memory::clkout_freq() * 128000, si5351_vco_f, 7);
|
||||
} else {
|
||||
clock_generator.set_ms_frequency(clock_generator_output_clkout, portapack::persistent_memory::clkout_freq() * 1000, si5351_vco_f, 0);
|
||||
}
|
||||
} else {
|
||||
clock_generator.disable_output(clock_generator_output_clkout);
|
||||
}
|
||||
|
||||
if(enable)
|
||||
clock_generator.set_clock_control(clock_generator_output_clkout, si5351_clock_control_common[clock_generator_output_clkout].ms_src(get_reference_clock_generator_pll(reference.source)).clk_pdn(ClockControl::ClockPowerDown::Power_On));
|
||||
else
|
||||
clock_generator.set_clock_control(clock_generator_output_clkout, ClockControl::power_off());
|
||||
}
|
||||
|
||||
@@ -79,6 +79,8 @@ public:
|
||||
|
||||
Reference get_reference() const;
|
||||
|
||||
void enable_clock_output(bool enable);
|
||||
|
||||
private:
|
||||
I2C& i2c0;
|
||||
si5351::Si5351& clock_generator;
|
||||
|
||||
@@ -38,9 +38,13 @@ void make_aprs_frame(const char * src_address, const uint32_t src_ssid,
|
||||
char address[14] = { 0 };
|
||||
|
||||
memcpy(&address[0], dest_address, 6);
|
||||
address[6] = (dest_ssid & 15) << 1;
|
||||
memcpy(&address[7], src_address, 6);
|
||||
address[13] = (src_ssid & 15) << 1;
|
||||
//euquiq: According to ax.25 doc section 2.2.13.x.x and 2.4.1.2
|
||||
// SSID need bits 5.6 set, so later when shifted it will end up being 011xxxx0 (xxxx = SSID number)
|
||||
// Notice that if need to signal usage of AX.25 V2.0, (dest_ssid | 112); (MSb will need to be set at the end)
|
||||
address[6] = (dest_ssid | 48);
|
||||
address[13] = (src_ssid | 48);
|
||||
|
||||
|
||||
frame.make_ui_frame(address, 0x03, protocol_id_t::NO_LAYER3, payload);
|
||||
}
|
||||
|
||||
@@ -51,32 +51,37 @@ void AX25Frame::NRZI_add_bit(const uint32_t bit) {
|
||||
}
|
||||
}
|
||||
|
||||
void AX25Frame::add_byte(uint8_t byte, bool is_flag, bool is_data) {
|
||||
uint32_t bit;
|
||||
|
||||
void AX25Frame::add_byte(uint8_t byte, bool is_flag, bool is_data)
|
||||
{
|
||||
bool bit;
|
||||
|
||||
if (is_data)
|
||||
crc_ccitt.process_byte(byte);
|
||||
|
||||
for (uint32_t i = 0; i < 8; i++) {
|
||||
|
||||
for (uint32_t i = 0; i < 8; i++)
|
||||
{
|
||||
bit = (byte >> i) & 1;
|
||||
|
||||
|
||||
NRZI_add_bit(bit);
|
||||
|
||||
if (bit)
|
||||
{
|
||||
ones_counter++;
|
||||
if ((ones_counter == 5) && (!is_flag))
|
||||
{
|
||||
NRZI_add_bit(0);
|
||||
ones_counter = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
ones_counter = 0;
|
||||
|
||||
if ((ones_counter == 6) && (!is_flag)) {
|
||||
NRZI_add_bit(0);
|
||||
ones_counter = 0;
|
||||
}
|
||||
|
||||
NRZI_add_bit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
void AX25Frame::flush() {
|
||||
void AX25Frame::flush()
|
||||
{
|
||||
if (bit_counter)
|
||||
*bb_data_ptr = current_byte << (7 - bit_counter);
|
||||
*bb_data_ptr = current_byte << (8 - bit_counter); //euquiq: This was 7 but there are 8 bits
|
||||
};
|
||||
|
||||
void AX25Frame::add_flag() {
|
||||
|
||||
@@ -94,13 +94,16 @@ void FrequencyScale::set_spectrum_sampling_rate(const int new_sampling_rate) {
|
||||
}
|
||||
|
||||
void FrequencyScale::set_channel_filter(
|
||||
const int pass_frequency,
|
||||
const int stop_frequency
|
||||
const int low_frequency,
|
||||
const int high_frequency,
|
||||
const int transition
|
||||
) {
|
||||
if( (channel_filter_pass_frequency != pass_frequency) ||
|
||||
(channel_filter_stop_frequency != stop_frequency) ) {
|
||||
channel_filter_pass_frequency = pass_frequency;
|
||||
channel_filter_stop_frequency = stop_frequency;
|
||||
if( (channel_filter_low_frequency != low_frequency) ||
|
||||
(channel_filter_high_frequency != high_frequency) ||
|
||||
(channel_filter_transition != transition) ) {
|
||||
channel_filter_low_frequency = low_frequency;
|
||||
channel_filter_high_frequency = high_frequency;
|
||||
channel_filter_transition = transition;
|
||||
set_dirty();
|
||||
}
|
||||
}
|
||||
@@ -184,41 +187,28 @@ void FrequencyScale::draw_frequency_ticks(Painter& painter, const Rect r) {
|
||||
}
|
||||
|
||||
void FrequencyScale::draw_filter_ranges(Painter& painter, const Rect r) {
|
||||
if( channel_filter_pass_frequency ) {
|
||||
if( channel_filter_low_frequency != channel_filter_high_frequency ) {
|
||||
const auto x_center = r.width() / 2;
|
||||
|
||||
const auto pass_offset = channel_filter_pass_frequency * spectrum_bins / spectrum_sampling_rate;
|
||||
const auto stop_offset = channel_filter_stop_frequency * spectrum_bins / spectrum_sampling_rate;
|
||||
const auto x_low = x_center + channel_filter_low_frequency * spectrum_bins / spectrum_sampling_rate;
|
||||
const auto x_high = x_center + channel_filter_high_frequency * spectrum_bins / spectrum_sampling_rate;
|
||||
|
||||
const auto pass_x_lo = x_center - pass_offset;
|
||||
const auto pass_x_hi = x_center + pass_offset;
|
||||
if( channel_filter_transition ) {
|
||||
const auto trans = channel_filter_transition * spectrum_bins / spectrum_sampling_rate;
|
||||
|
||||
if( channel_filter_stop_frequency ) {
|
||||
const auto stop_x_lo = x_center - stop_offset;
|
||||
const auto stop_x_hi = x_center + stop_offset;
|
||||
|
||||
const Rect r_stop_lo {
|
||||
r.left() + stop_x_lo, r.bottom() - filter_band_height,
|
||||
pass_x_lo - stop_x_lo, filter_band_height
|
||||
const Rect r_all {
|
||||
r.left() + x_low - trans, r.bottom() - filter_band_height,
|
||||
x_high - x_low + trans*2, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_stop_lo,
|
||||
Color::yellow()
|
||||
);
|
||||
|
||||
const Rect r_stop_hi {
|
||||
r.left() + pass_x_hi, r.bottom() - filter_band_height,
|
||||
stop_x_hi - pass_x_hi, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_stop_hi,
|
||||
r_all,
|
||||
Color::yellow()
|
||||
);
|
||||
}
|
||||
|
||||
const Rect r_pass {
|
||||
r.left() + pass_x_lo, r.bottom() - filter_band_height,
|
||||
pass_x_hi - pass_x_lo, filter_band_height
|
||||
r.left() + x_low, r.bottom() - filter_band_height,
|
||||
x_high - x_low, filter_band_height
|
||||
};
|
||||
painter.fill_rectangle(
|
||||
r_pass,
|
||||
@@ -390,8 +380,9 @@ void WaterfallWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) {
|
||||
sampling_rate = spectrum.sampling_rate;
|
||||
frequency_scale.set_spectrum_sampling_rate(sampling_rate);
|
||||
frequency_scale.set_channel_filter(
|
||||
spectrum.channel_filter_pass_frequency,
|
||||
spectrum.channel_filter_stop_frequency
|
||||
spectrum.channel_filter_low_frequency,
|
||||
spectrum.channel_filter_high_frequency,
|
||||
spectrum.channel_filter_transition
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ public:
|
||||
bool on_key(const KeyEvent key) override;
|
||||
|
||||
void set_spectrum_sampling_rate(const int new_sampling_rate);
|
||||
void set_channel_filter(const int pass_frequency, const int stop_frequency);
|
||||
void set_channel_filter(const int low_frequency, const int high_frequency, const int transition);
|
||||
|
||||
void paint(Painter& painter) override;
|
||||
|
||||
@@ -96,8 +96,9 @@ private:
|
||||
SignalToken signal_token_tick_second { };
|
||||
int spectrum_sampling_rate { 0 };
|
||||
const int spectrum_bins = std::tuple_size<decltype(ChannelSpectrum::db)>::value;
|
||||
int channel_filter_pass_frequency { 0 };
|
||||
int channel_filter_stop_frequency { 0 };
|
||||
int channel_filter_low_frequency { 0 };
|
||||
int channel_filter_high_frequency { 0 };
|
||||
int channel_filter_transition { 0 };
|
||||
|
||||
void clear();
|
||||
void clear_background(Painter& painter, const Rect r);
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
#include "tpms_app.hpp"
|
||||
|
||||
#include "core_control.hpp"
|
||||
|
||||
#include "ui_looking_glass_app.hpp"
|
||||
#include "file.hpp"
|
||||
#include "png_writer.hpp"
|
||||
|
||||
@@ -113,7 +113,7 @@ SystemStatusView::SystemStatusView(
|
||||
&button_camera,
|
||||
&button_sleep,
|
||||
&button_bias_tee,
|
||||
&image_clock_status,
|
||||
&button_clock_status,
|
||||
&sd_card_status_view,
|
||||
});
|
||||
|
||||
@@ -168,6 +168,10 @@ SystemStatusView::SystemStatusView(
|
||||
DisplaySleepMessage message;
|
||||
EventDispatcher::send_message(message);
|
||||
};
|
||||
|
||||
button_clock_status.on_select = [this](ImageButton&) {
|
||||
this->on_clk();
|
||||
};
|
||||
}
|
||||
|
||||
void SystemStatusView::refresh() {
|
||||
@@ -188,11 +192,16 @@ void SystemStatusView::refresh() {
|
||||
}
|
||||
|
||||
if (portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::External) {
|
||||
image_clock_status.set_bitmap(&bitmap_icon_clk_ext);
|
||||
button_bias_tee.set_foreground(ui::Color::green());
|
||||
button_clock_status.set_bitmap(&bitmap_icon_clk_ext);
|
||||
// button_bias_tee.set_foreground(ui::Color::green()); Typo?
|
||||
} else {
|
||||
image_clock_status.set_bitmap(&bitmap_icon_clk_int);
|
||||
button_bias_tee.set_foreground(ui::Color::light_grey());
|
||||
button_clock_status.set_bitmap(&bitmap_icon_clk_int);
|
||||
// button_bias_tee.set_foreground(ui::Color::green());
|
||||
}
|
||||
if(portapack::persistent_memory::clkout_enabled()) {
|
||||
button_clock_status.set_foreground(ui::Color::green());
|
||||
} else {
|
||||
button_clock_status.set_foreground(ui::Color::light_grey());
|
||||
}
|
||||
|
||||
set_dirty();
|
||||
@@ -302,6 +311,18 @@ void SystemStatusView::on_camera() {
|
||||
}
|
||||
}
|
||||
|
||||
void SystemStatusView::on_clk() {
|
||||
bool v = portapack::persistent_memory::clkout_enabled();
|
||||
if(v) {
|
||||
v = false;
|
||||
} else {
|
||||
v = true;
|
||||
}
|
||||
portapack::clock_manager.enable_clock_output(v);
|
||||
portapack::persistent_memory::set_clkout_enabled(v);
|
||||
refresh();
|
||||
}
|
||||
|
||||
void SystemStatusView::on_title() {
|
||||
if(nav_.is_top())
|
||||
nav_.push<AboutView>();
|
||||
@@ -464,10 +485,10 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
|
||||
add_items({
|
||||
//{ "..", ui::Color::light_grey(),&bitmap_icon_previous, [&nav](){ nav.pop(); } },
|
||||
{ "ADS-B [S]", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ADSBTxView>(); } },
|
||||
{ "APRS", ui::Color::orange(), &bitmap_icon_aprs, [&nav](){ nav.push<APRSTXView>(); } },
|
||||
{ "APRS", ui::Color::green(), &bitmap_icon_aprs, [&nav](){ nav.push<APRSTXView>(); } },
|
||||
{ "BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav](){ nav.push<BHTView>(); } },
|
||||
{ "GPS Sim", ui::Color::yellow(), &bitmap_icon_gps_sim, [&nav](){ nav.push<GpsSimAppView>(); } },
|
||||
{ "Jammer", ui::Color::yellow(), &bitmap_icon_jammer, [&nav](){ nav.push<JammerView>(); } },
|
||||
{ "Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav](){ nav.push<JammerView>(); } },
|
||||
{ "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push<KeyfobView>(); } },
|
||||
{ "LGE tool", ui::Color::yellow(), &bitmap_icon_lge, [&nav](){ nav.push<LGEView>(); } },
|
||||
{ "Morse", ui::Color::green(), &bitmap_icon_morse, [&nav](){ nav.push<MorseView>(); } },
|
||||
@@ -522,8 +543,9 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
|
||||
{ "Capture", ui::Color::red(), &bitmap_icon_capture, [&nav](){ nav.push<CaptureAppView>(); } },
|
||||
{ "Replay", ui::Color::green(), &bitmap_icon_replay, [&nav](){ nav.push<ReplayAppView>(); } },
|
||||
{ "Calls", ui::Color::yellow(), &bitmap_icon_search, [&nav](){ nav.push<SearchView>(); } },
|
||||
{ "Scanner", ui::Color::green(), &bitmap_icon_scanner, [&nav](){ nav.push<ScannerView>(); } },
|
||||
{ "Scanner", ui::Color::yellow(), &bitmap_icon_scanner, [&nav](){ nav.push<ScannerView>(); } },
|
||||
{ "Microphone", ui::Color::yellow(), &bitmap_icon_microphone,[&nav](){ nav.push<MicTXView>(); } },
|
||||
{ "Looking Glass", ui::Color::yellow(), &bitmap_icon_looking, [&nav](){ nav.push<GlassView>(); } },
|
||||
{ "Tools", ui::Color::cyan(), &bitmap_icon_utilities, [&nav](){ nav.push<UtilitiesMenuView>(); } },
|
||||
{ "Options", ui::Color::cyan(), &bitmap_icon_setup, [&nav](){ nav.push<SettingsMenuView>(); } },
|
||||
{ "Debug", ui::Color::light_grey(), &bitmap_icon_debug, [&nav](){ nav.push<DebugMenuView>(); } },
|
||||
|
||||
@@ -180,7 +180,7 @@ private:
|
||||
Color::dark_grey()
|
||||
};
|
||||
|
||||
Image image_clock_status {
|
||||
ImageButton button_clock_status {
|
||||
{ 27 * 8, 0 * 16, 2 * 8, 1 * 16 },
|
||||
&bitmap_icon_clk_int,
|
||||
Color::light_grey(),
|
||||
@@ -198,6 +198,7 @@ private:
|
||||
void on_camera();
|
||||
void on_title();
|
||||
void refresh();
|
||||
void on_clk();
|
||||
|
||||
MessageHandlerRegistration message_handler_refresh {
|
||||
Message::ID::StatusRefresh,
|
||||
@@ -213,7 +214,7 @@ public:
|
||||
InformationView(NavigationView& nav);
|
||||
|
||||
private:
|
||||
static constexpr auto version_string = "v1.2.3";
|
||||
static constexpr auto version_string = "v1.3.0";
|
||||
NavigationView& nav_;
|
||||
|
||||
Rectangle backdrop {
|
||||
|
||||
@@ -35,7 +35,7 @@ void NarrowbandAMAudio::execute(const buffer_c8_t& buffer) {
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
|
||||
|
||||
channel_spectrum.feed(decim_1_out, channel_filter_pass_f, channel_filter_stop_f);
|
||||
channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
|
||||
|
||||
const auto decim_2_out = decim_2.execute(decim_1_out, dst_buffer);
|
||||
const auto channel_out = channel_filter.execute(decim_2_out, dst_buffer);
|
||||
@@ -93,8 +93,9 @@ void NarrowbandAMAudio::configure(const AMConfigureMessage& message) {
|
||||
decim_1.configure(message.decim_1_filter.taps, 131072);
|
||||
decim_2.configure(message.decim_2_filter.taps, decim_2_decimation_factor);
|
||||
channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor);
|
||||
channel_filter_pass_f = message.channel_filter.pass_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_stop_f = message.channel_filter.stop_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs;
|
||||
channel_spectrum.set_decimation_factor(1.0f);
|
||||
modulation_ssb = (message.modulation == AMConfigureMessage::Modulation::SSB);
|
||||
audio_output.configure(message.audio_hpf_config);
|
||||
|
||||
@@ -64,8 +64,9 @@ private:
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
|
||||
dsp::decimate::FIRAndDecimateComplex decim_2 { };
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter { };
|
||||
uint32_t channel_filter_pass_f = 0;
|
||||
uint32_t channel_filter_stop_f = 0;
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
int32_t channel_filter_transition = 0;
|
||||
|
||||
bool modulation_ssb = false;
|
||||
dsp::demodulate::AM demod_am { };
|
||||
|
||||
@@ -52,7 +52,7 @@ void CaptureProcessor::execute(const buffer_c8_t& buffer) {
|
||||
spectrum_samples += channel.count;
|
||||
if( spectrum_samples >= spectrum_interval_samples ) {
|
||||
spectrum_samples -= spectrum_interval_samples;
|
||||
channel_spectrum.feed(channel, channel_filter_pass_f, channel_filter_stop_f);
|
||||
channel_spectrum.feed(channel, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +85,9 @@ void CaptureProcessor::samplerate_config(const SamplerateConfigMessage& message)
|
||||
size_t decim_1_input_fs = decim_0_output_fs;
|
||||
size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor;
|
||||
|
||||
channel_filter_pass_f = taps_200k_decim_1.pass_frequency_normalized * decim_1_input_fs; // 162760.416666667
|
||||
channel_filter_stop_f = taps_200k_decim_1.stop_frequency_normalized * decim_1_input_fs; // 337239.583333333
|
||||
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * decim_1_input_fs;
|
||||
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * decim_1_input_fs;
|
||||
channel_filter_transition = taps_200k_decim_1.transition_normalized * decim_1_input_fs;
|
||||
|
||||
spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz;
|
||||
spectrum_samples = 0;
|
||||
|
||||
@@ -60,8 +60,9 @@ private:
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x16Decim2 decim_1 { };
|
||||
uint32_t channel_filter_pass_f = 0;
|
||||
uint32_t channel_filter_stop_f = 0;
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
int32_t channel_filter_transition = 0;
|
||||
|
||||
std::unique_ptr<StreamInput> stream { };
|
||||
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
#include "utility.hpp"
|
||||
|
||||
ReplayProcessor::ReplayProcessor() {
|
||||
channel_filter_pass_f = taps_200k_decim_1.pass_frequency_normalized * 1000000; // 162760.416666667
|
||||
channel_filter_stop_f = taps_200k_decim_1.stop_frequency_normalized * 1000000; // 337239.583333333
|
||||
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * 1000000;
|
||||
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * 1000000;
|
||||
channel_filter_transition = taps_200k_decim_1.transition_normalized * 1000000;
|
||||
|
||||
spectrum_samples = 0;
|
||||
|
||||
|
||||
@@ -55,8 +55,9 @@ private:
|
||||
baseband_fs
|
||||
};
|
||||
|
||||
uint32_t channel_filter_pass_f = 0;
|
||||
uint32_t channel_filter_stop_f = 0;
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
int32_t channel_filter_transition = 0;
|
||||
|
||||
std::unique_ptr<StreamOutput> stream { };
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ void NarrowbandFMAudio::execute(const buffer_c8_t& buffer) {
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer);
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
|
||||
|
||||
channel_spectrum.feed(decim_1_out, channel_filter_pass_f, channel_filter_stop_f);
|
||||
channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
|
||||
|
||||
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
|
||||
|
||||
@@ -145,8 +145,9 @@ void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) {
|
||||
decim_1.configure(message.decim_1_filter.taps, 131072);
|
||||
channel_filter.configure(message.channel_filter.taps, message.channel_decimation);
|
||||
demod.configure(demod_input_fs, message.deviation);
|
||||
channel_filter_pass_f = message.channel_filter.pass_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_stop_f = message.channel_filter.stop_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs;
|
||||
channel_spectrum.set_decimation_factor(1.0f);
|
||||
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, (float)message.squelch_level / 100.0);
|
||||
|
||||
|
||||
@@ -73,8 +73,9 @@ private:
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1 { };
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter { };
|
||||
uint32_t channel_filter_pass_f = 0;
|
||||
uint32_t channel_filter_stop_f = 0;
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
int32_t channel_filter_transition = 0;
|
||||
|
||||
// For CTCSS decoding
|
||||
dsp::decimate::FIR64AndDecimateBy2Real ctcss_filter { };
|
||||
|
||||
@@ -29,8 +29,9 @@
|
||||
#include "utility.hpp"
|
||||
|
||||
ReplayProcessor::ReplayProcessor() {
|
||||
channel_filter_pass_f = taps_200k_decim_1.pass_frequency_normalized * 1000000; // 162760.416666667
|
||||
channel_filter_stop_f = taps_200k_decim_1.stop_frequency_normalized * 1000000; // 337239.583333333
|
||||
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * 1000000;
|
||||
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * 1000000;
|
||||
channel_filter_transition = taps_200k_decim_1.transition_normalized * 1000000;
|
||||
|
||||
spectrum_samples = 0;
|
||||
|
||||
@@ -70,7 +71,7 @@ void ReplayProcessor::execute(const buffer_c8_t& buffer) {
|
||||
spectrum_samples += buffer.count;
|
||||
if( spectrum_samples >= spectrum_interval_samples ) {
|
||||
spectrum_samples -= spectrum_interval_samples;
|
||||
channel_spectrum.feed(iq_buffer, channel_filter_pass_f, channel_filter_stop_f);
|
||||
channel_spectrum.feed(iq_buffer, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
|
||||
|
||||
txprogress_message.progress = bytes_read; // Inform UI about progress
|
||||
txprogress_message.done = false;
|
||||
|
||||
@@ -54,8 +54,9 @@ private:
|
||||
baseband_fs / 8
|
||||
};
|
||||
|
||||
uint32_t channel_filter_pass_f = 0;
|
||||
uint32_t channel_filter_stop_f = 0;
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
int32_t channel_filter_transition = 0;
|
||||
|
||||
std::unique_ptr<StreamOutput> stream { };
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ void WidebandFMAudio::execute(const buffer_c8_t& buffer) {
|
||||
spectrum_samples += channel.count;
|
||||
if( spectrum_samples >= spectrum_interval_samples ) {
|
||||
spectrum_samples -= spectrum_interval_samples;
|
||||
channel_spectrum.feed(channel, channel_filter_pass_f, channel_filter_stop_f);
|
||||
channel_spectrum.feed(channel, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
|
||||
}
|
||||
|
||||
/* 384kHz complex<int16_t>[256]
|
||||
@@ -169,8 +169,9 @@ void WidebandFMAudio::configure(const WFMConfigureMessage& message) {
|
||||
|
||||
decim_0.configure(message.decim_0_filter.taps, 33554432);
|
||||
decim_1.configure(message.decim_1_filter.taps, 131072);
|
||||
channel_filter_pass_f = message.decim_1_filter.pass_frequency_normalized * decim_1_input_fs;
|
||||
channel_filter_stop_f = message.decim_1_filter.stop_frequency_normalized * decim_1_input_fs;
|
||||
channel_filter_low_f = message.decim_1_filter.low_frequency_normalized * decim_1_input_fs;
|
||||
channel_filter_high_f = message.decim_1_filter.high_frequency_normalized * decim_1_input_fs;
|
||||
channel_filter_transition = message.decim_1_filter.transition_normalized * decim_1_input_fs;
|
||||
demod.configure(demod_input_fs, message.deviation);
|
||||
audio_filter.configure(message.audio_filter.taps);
|
||||
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config);
|
||||
|
||||
@@ -67,8 +67,9 @@ private:
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { };
|
||||
dsp::decimate::FIRC16xR16x16Decim2 decim_1 { };
|
||||
uint32_t channel_filter_pass_f = 0;
|
||||
uint32_t channel_filter_stop_f = 0;
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
int32_t channel_filter_transition = 0;
|
||||
|
||||
dsp::demodulate::FM demod { };
|
||||
dsp::decimate::DecimateBy2CIC4Real audio_dec_1 { };
|
||||
|
||||
@@ -53,7 +53,7 @@ void WidebandSpectrum::execute(const buffer_c8_t& buffer) {
|
||||
};
|
||||
channel_spectrum.feed(
|
||||
buffer_c16,
|
||||
0, 0
|
||||
0, 0, 0
|
||||
);
|
||||
phase = 0;
|
||||
} else {
|
||||
|
||||
@@ -78,12 +78,14 @@ void SpectrumCollector::set_decimation_factor(
|
||||
|
||||
void SpectrumCollector::feed(
|
||||
const buffer_c16_t& channel,
|
||||
const uint32_t filter_pass_frequency,
|
||||
const uint32_t filter_stop_frequency
|
||||
const int32_t filter_low_frequency,
|
||||
const int32_t filter_high_frequency,
|
||||
const int32_t filter_transition
|
||||
) {
|
||||
// Called from baseband processing thread.
|
||||
channel_filter_pass_frequency = filter_pass_frequency;
|
||||
channel_filter_stop_frequency = filter_stop_frequency;
|
||||
channel_filter_low_frequency = filter_low_frequency;
|
||||
channel_filter_high_frequency = filter_high_frequency;
|
||||
channel_filter_transition = filter_transition;
|
||||
|
||||
channel_spectrum_decimator.feed(
|
||||
channel,
|
||||
@@ -136,8 +138,9 @@ void SpectrumCollector::update() {
|
||||
|
||||
ChannelSpectrum spectrum;
|
||||
spectrum.sampling_rate = channel_spectrum_sampling_rate;
|
||||
spectrum.channel_filter_pass_frequency = channel_filter_pass_frequency;
|
||||
spectrum.channel_filter_stop_frequency = channel_filter_stop_frequency;
|
||||
spectrum.channel_filter_low_frequency = channel_filter_low_frequency;
|
||||
spectrum.channel_filter_high_frequency = channel_filter_high_frequency;
|
||||
spectrum.channel_filter_transition = channel_filter_transition;
|
||||
for(size_t i=0; i<spectrum.db.size(); i++) {
|
||||
const auto corrected_sample = spectrum_window_hamming_3(channel_spectrum, i);
|
||||
const auto mag2 = magnitude_squared(corrected_sample * (1.0f / 32768.0f));
|
||||
|
||||
@@ -40,8 +40,9 @@ public:
|
||||
|
||||
void feed(
|
||||
const buffer_c16_t& channel,
|
||||
const uint32_t filter_pass_frequency,
|
||||
const uint32_t filter_stop_frequency
|
||||
const int32_t filter_low_frequency,
|
||||
const int32_t filter_high_frequency,
|
||||
const int32_t filter_transition
|
||||
);
|
||||
|
||||
private:
|
||||
@@ -53,8 +54,9 @@ private:
|
||||
bool streaming { false };
|
||||
std::array<std::complex<float>, 256> channel_spectrum { };
|
||||
uint32_t channel_spectrum_sampling_rate { 0 };
|
||||
uint32_t channel_filter_pass_frequency { 0 };
|
||||
uint32_t channel_filter_stop_frequency { 0 };
|
||||
int32_t channel_filter_low_frequency { 0 };
|
||||
int32_t channel_filter_high_frequency { 0 };
|
||||
int32_t channel_filter_transition { 0 };
|
||||
|
||||
void post_message(const buffer_c16_t& data);
|
||||
|
||||
|
||||
@@ -30,15 +30,17 @@
|
||||
|
||||
template<size_t N>
|
||||
struct fir_taps_real {
|
||||
float pass_frequency_normalized;
|
||||
float stop_frequency_normalized;
|
||||
float low_frequency_normalized;
|
||||
float high_frequency_normalized;
|
||||
float transition_normalized;
|
||||
std::array<int16_t, N> taps;
|
||||
};
|
||||
|
||||
template<size_t N>
|
||||
struct fir_taps_complex {
|
||||
float pass_frequency_normalized;
|
||||
float stop_frequency_normalized;
|
||||
float low_frequency_normalized;
|
||||
float high_frequency_normalized;
|
||||
float transition_normalized;
|
||||
std::array<complex16_t, N> taps;
|
||||
};
|
||||
|
||||
@@ -46,8 +48,9 @@ struct fir_taps_complex {
|
||||
|
||||
// IFIR image-reject filter: fs=3072000, pass=8000, stop=344000, decim=8, fout=384000
|
||||
constexpr fir_taps_real<24> taps_16k0_decim_0 {
|
||||
.pass_frequency_normalized = 8000.0f / 3072000.0f,
|
||||
.stop_frequency_normalized = 344000.0f / 3072000.0f,
|
||||
.low_frequency_normalized = -8000.0f / 3072000.0f,
|
||||
.high_frequency_normalized = 8000.0f / 3072000.0f,
|
||||
.transition_normalized = 336000.0f / 3072000.0f,
|
||||
.taps = { {
|
||||
1, 67, 165, 340, 599, 944, 1361, 1820,
|
||||
2278, 2684, 2988, 3152, 3152, 2988, 2684, 2278,
|
||||
@@ -57,8 +60,9 @@ constexpr fir_taps_real<24> taps_16k0_decim_0 {
|
||||
|
||||
// IFIR prototype filter: fs=384000, pass=8000, stop=40000, decim=8, fout=48000
|
||||
constexpr fir_taps_real<32> taps_16k0_decim_1 {
|
||||
.pass_frequency_normalized = 8000.0f / 384000.0f,
|
||||
.stop_frequency_normalized = 40000.0f / 384000.0f,
|
||||
.low_frequency_normalized = -8000.0f / 384000.0f,
|
||||
.high_frequency_normalized = 8000.0f / 384000.0f,
|
||||
.transition_normalized = 32000.0f / 384000.0f,
|
||||
.taps = { {
|
||||
-26, -125, -180, -275, -342, -359, -286, -90,
|
||||
250, 733, 1337, 2011, 2688, 3289, 3740, 3982,
|
||||
@@ -69,8 +73,9 @@ constexpr fir_taps_real<32> taps_16k0_decim_1 {
|
||||
|
||||
// Channel filter: fs=48000, pass=8000, stop=12400, decim=1, fout=48000
|
||||
constexpr fir_taps_real<32> taps_16k0_channel {
|
||||
.pass_frequency_normalized = 8000.0f / 48000.0f,
|
||||
.stop_frequency_normalized = 12400.0f / 48000.0f,
|
||||
.low_frequency_normalized = -8000.0f / 48000.0f,
|
||||
.high_frequency_normalized = 8000.0f / 48000.0f,
|
||||
.transition_normalized = 4400.0f / 48000.0f,
|
||||
.taps = { {
|
||||
-73, -285, -376, -8, 609, 538, -584, -1387,
|
||||
-148, 2173, 1959, -2146, -5267, -297, 12915, 24737,
|
||||
@@ -83,8 +88,9 @@ constexpr fir_taps_real<32> taps_16k0_channel {
|
||||
|
||||
// IFIR image-reject filter: fs=3072000, pass=5500, stop=341500, decim=8, fout=384000
|
||||
constexpr fir_taps_real<24> taps_11k0_decim_0 {
|
||||
.pass_frequency_normalized = 5500.0f / 3072000.0f,
|
||||
.stop_frequency_normalized = 341500.0f / 3072000.0f,
|
||||
.low_frequency_normalized = -5500.0f / 3072000.0f,
|
||||
.high_frequency_normalized = 5500.0f / 3072000.0f,
|
||||
.transition_normalized = 336000.0f / 3072000.0f,
|
||||
.taps = { {
|
||||
38, 102, 220, 406, 668, 1004, 1397, 1822,
|
||||
2238, 2603, 2875, 3020, 3020, 2875, 2603, 2238,
|
||||
@@ -94,8 +100,9 @@ constexpr fir_taps_real<24> taps_11k0_decim_0 {
|
||||
|
||||
// IFIR prototype filter: fs=384000, pass=5500, stop=42500, decim=8, fout=48000
|
||||
constexpr fir_taps_real<32> taps_11k0_decim_1 {
|
||||
.pass_frequency_normalized = 5500.0f / 384000.0f,
|
||||
.stop_frequency_normalized = 42500.0f / 384000.0f,
|
||||
.low_frequency_normalized = -5500.0f / 384000.0f,
|
||||
.high_frequency_normalized = 5500.0f / 384000.0f,
|
||||
.transition_normalized = 37000.0f / 384000.0f,
|
||||
.taps = { {
|
||||
-42, -87, -157, -234, -298, -318, -255, -75,
|
||||
246, 713, 1306, 1976, 2656, 3265, 3724, 3971,
|
||||
@@ -106,8 +113,9 @@ constexpr fir_taps_real<32> taps_11k0_decim_1 {
|
||||
|
||||
// Channel filter: fs=48000, pass=5500, stop=8900, decim=1, fout=48000
|
||||
constexpr fir_taps_real<32> taps_11k0_channel {
|
||||
.pass_frequency_normalized = 5500.0f / 48000.0f,
|
||||
.stop_frequency_normalized = 8900.0f / 48000.0f,
|
||||
.low_frequency_normalized = -5500.0f / 48000.0f,
|
||||
.high_frequency_normalized = 5500.0f / 48000.0f,
|
||||
.transition_normalized = 3400.0f / 48000.0f,
|
||||
.taps = { {
|
||||
-68, -345, -675, -867, -582, 247, 1222, 1562,
|
||||
634, -1379, -3219, -3068, 310, 6510, 13331, 17795,
|
||||
@@ -120,8 +128,9 @@ constexpr fir_taps_real<32> taps_11k0_channel {
|
||||
|
||||
// IFIR image-reject filter: fs=3072000, pass=4250, stop=340250, decim=8, fout=384000
|
||||
constexpr fir_taps_real<24> taps_4k25_decim_0 {
|
||||
.pass_frequency_normalized = 4250.0f / 3072000.0f,
|
||||
.stop_frequency_normalized = 340250.0f / 3072000.0f,
|
||||
.low_frequency_normalized = -4250.0f / 3072000.0f,
|
||||
.high_frequency_normalized = 4250.0f / 3072000.0f,
|
||||
.transition_normalized = 33600.0f / 3072000.0f,
|
||||
.taps = { {
|
||||
38, 103, 222, 409, 671, 1006, 1399, 1821,
|
||||
2236, 2599, 2868, 3012, 3012, 2868, 2599, 2236,
|
||||
@@ -131,8 +140,9 @@ constexpr fir_taps_real<24> taps_4k25_decim_0 {
|
||||
|
||||
// IFIR prototype filter: fs=384000, pass=4250, stop=43750, decim=8, fout=48000
|
||||
constexpr fir_taps_real<32> taps_4k25_decim_1 {
|
||||
.pass_frequency_normalized = 4250.0f / 384000.0f,
|
||||
.stop_frequency_normalized = 43750.0f / 384000.0f,
|
||||
.low_frequency_normalized = -4250.0f / 384000.0f,
|
||||
.high_frequency_normalized = 4250.0f / 384000.0f,
|
||||
.transition_normalized = 39500.0f / 384000.0f,
|
||||
.taps = { {
|
||||
-33, -74, -139, -214, -280, -306, -254, -87,
|
||||
222, 682, 1274, 1951, 2644, 3268, 3741, 3996,
|
||||
@@ -143,8 +153,9 @@ constexpr fir_taps_real<32> taps_4k25_decim_1 {
|
||||
|
||||
// Channel filter: fs=48000, pass=4250, stop=7900, decim=1, fout=48000
|
||||
constexpr fir_taps_real<32> taps_4k25_channel {
|
||||
.pass_frequency_normalized = 4250.0f / 48000.0f,
|
||||
.stop_frequency_normalized = 7900.0f / 48000.0f,
|
||||
.low_frequency_normalized = -4250.0f / 48000.0f,
|
||||
.high_frequency_normalized = 4250.0f / 48000.0f,
|
||||
.transition_normalized = 3650.0f / 48000.0f,
|
||||
.taps = { {
|
||||
-58, -14, 153, 484, 871, 1063, 770, -141,
|
||||
-1440, -2488, -2435, -614, 3035, 7771, 12226, 14927,
|
||||
@@ -161,8 +172,6 @@ constexpr fir_taps_real<32> taps_4k25_channel {
|
||||
* sum(abs(taps)): 125270
|
||||
*/
|
||||
/*constexpr fir_taps_real<64> taps_64_lp_025_025 {
|
||||
.pass_frequency_normalized = 0.025f,
|
||||
.stop_frequency_normalized = 0.025f,
|
||||
.taps = { {
|
||||
0, 0, -3, -7, -13, -20, -27, -32,
|
||||
-34, -33, -25, -10, 13, 47, 94, 152,
|
||||
@@ -183,8 +192,9 @@ constexpr fir_taps_real<32> taps_4k25_channel {
|
||||
* sum(abs(taps)): 125270
|
||||
*/
|
||||
constexpr fir_taps_real<64> taps_64_lp_025_025 {
|
||||
.pass_frequency_normalized = 0.0125f,
|
||||
.stop_frequency_normalized = 0.0125f,
|
||||
.low_frequency_normalized = 0,
|
||||
.high_frequency_normalized = 0,
|
||||
.transition_normalized = 0,
|
||||
.taps = { {
|
||||
0, 0, 2, 6, 12, 20, 32, 46,
|
||||
64, 85, 110, 138, 169, 204, 241, 281,
|
||||
@@ -201,8 +211,9 @@ constexpr fir_taps_real<64> taps_64_lp_025_025 {
|
||||
|
||||
// IFIR image-reject filter: fs=3072000, pass=3000, stop=339000, decim=8, fout=384000
|
||||
constexpr fir_taps_real<24> taps_6k0_decim_0 {
|
||||
.pass_frequency_normalized = 3000.0f / 3072000.0f,
|
||||
.stop_frequency_normalized = 339000.0f / 3072000.0f,
|
||||
.low_frequency_normalized = -3000.0f / 3072000.0f,
|
||||
.high_frequency_normalized = 3000.0f / 3072000.0f,
|
||||
.transition_normalized = 336000.0f / 3072000.0f,
|
||||
.taps = { {
|
||||
39, 104, 224, 412, 674, 1008, 1400, 1821,
|
||||
2234, 2594, 2863, 3006, 3006, 2863, 2594, 2234,
|
||||
@@ -212,8 +223,9 @@ constexpr fir_taps_real<24> taps_6k0_decim_0 {
|
||||
|
||||
// IFIR prototype filter: fs=384000, pass=3000, stop=45000, decim=8, fout=48000
|
||||
constexpr fir_taps_real<32> taps_6k0_decim_1 {
|
||||
.pass_frequency_normalized = 3000.0f / 384000.0f,
|
||||
.stop_frequency_normalized = 45000.0f / 384000.0f,
|
||||
.low_frequency_normalized = -3000.0f / 384000.0f,
|
||||
.high_frequency_normalized = 3000.0f / 384000.0f,
|
||||
.transition_normalized = 43000.0f / 384000.0f,
|
||||
.taps = { {
|
||||
-26, -63, -123, -195, -263, -295, -253, -99,
|
||||
199, 651, 1242, 1927, 2633, 3273, 3760, 4023,
|
||||
@@ -224,8 +236,9 @@ constexpr fir_taps_real<32> taps_6k0_decim_1 {
|
||||
|
||||
// IFIR prototype filter: fs=48000, pass=3000, stop=6700, decim=4, fout=12000
|
||||
constexpr fir_taps_real<32> taps_6k0_decim_2 {
|
||||
.pass_frequency_normalized = 3000.0f / 48000.0f,
|
||||
.stop_frequency_normalized = 6700.0f / 48000.0f,
|
||||
.low_frequency_normalized = -3000.0f / 48000.0f,
|
||||
.high_frequency_normalized = 3000.0f / 48000.0f,
|
||||
.transition_normalized = 3700.0f / 48000.0f,
|
||||
.taps = { {
|
||||
95, 178, 247, 208, -21, -474, -1080, -1640,
|
||||
-1857, -1411, -83, 2134, 4978, 7946, 10413, 11815,
|
||||
@@ -239,8 +252,9 @@ constexpr fir_taps_real<32> taps_6k0_decim_2 {
|
||||
* slightly larger than 32767 (33312).
|
||||
*/
|
||||
constexpr fir_taps_complex<64> taps_6k0_dsb_channel {
|
||||
.pass_frequency_normalized = 3000.0f / 12000.0f,
|
||||
.stop_frequency_normalized = 3300.0f / 12000.0f,
|
||||
.low_frequency_normalized = -3000.0f / 12000.0f,
|
||||
.high_frequency_normalized = 3000.0f / 12000.0f,
|
||||
.transition_normalized = 300.0f / 12000.0f,
|
||||
.taps = { {
|
||||
{ -69, 0 }, { -140, 0 }, { 119, 0 }, { 89, 0 },
|
||||
{ -132, 0 }, { -134, 0 }, { 197, 0 }, { 167, 0 },
|
||||
@@ -265,8 +279,9 @@ constexpr fir_taps_complex<64> taps_6k0_dsb_channel {
|
||||
|
||||
// IFIR prototype filter: fs=12000, pass=3000, stop=3300, decim=1, fout=12000
|
||||
constexpr fir_taps_complex<64> taps_2k8_usb_channel {
|
||||
.pass_frequency_normalized = 3000.0f / 12000.0f,
|
||||
.stop_frequency_normalized = 3300.0f / 12000.0f,
|
||||
.low_frequency_normalized = 0,
|
||||
.high_frequency_normalized = 3000.0f / 12000.0f,
|
||||
.transition_normalized = 300.0f / 12000.0f,
|
||||
.taps = { {
|
||||
{ -146, 0 }, { -41, -45 }, { -1, 10 }, { -95, 69 },
|
||||
{ -194, -41 }, { -91, -158 }, { 14, -43 }, { -150, 67 },
|
||||
@@ -291,8 +306,9 @@ constexpr fir_taps_complex<64> taps_2k8_usb_channel {
|
||||
|
||||
// IFIR prototype filter: fs=12000, pass=3000, stop=3300, decim=1, fout=12000
|
||||
constexpr fir_taps_complex<64> taps_2k8_lsb_channel {
|
||||
.pass_frequency_normalized = 3000.0f / 12000.0f,
|
||||
.stop_frequency_normalized = 3300.0f / 12000.0f,
|
||||
.low_frequency_normalized = -3000.0f / 12000.0f,
|
||||
.high_frequency_normalized = 0,
|
||||
.transition_normalized = 300.0f / 12000.0f,
|
||||
.taps = { {
|
||||
{ -146, 0 }, { -41, 45 }, { -1, -10 }, { -95, -69 },
|
||||
{ -194, 41 }, { -91, 158 }, { 14, 43 }, { -150, -67 },
|
||||
@@ -316,8 +332,9 @@ constexpr fir_taps_complex<64> taps_2k8_lsb_channel {
|
||||
// USB AM 700Hz filter: fs=12000, start=600, end=800, width=200, stop=40db, decim=1, fout=12000
|
||||
|
||||
constexpr fir_taps_complex<64> taps_0k7_usb_channel {
|
||||
.pass_frequency_normalized = 3000.0f / 12000.0f,
|
||||
.stop_frequency_normalized = 3300.0f / 12000.0f,
|
||||
.low_frequency_normalized = 600.0f / 12000.0f,
|
||||
.high_frequency_normalized = 800.0f / 12000.0f,
|
||||
.transition_normalized = 200.0f / 12000.0f,
|
||||
.taps = { {
|
||||
{ 531, 0 }, { 192, 73 }, { 181, 163 }, { 129, 254 },
|
||||
{ 34, 328 }, { -97, 364 }, { -251, 345 }, { -403, 261 },
|
||||
@@ -342,8 +359,9 @@ constexpr fir_taps_complex<64> taps_0k7_usb_channel {
|
||||
|
||||
// IFIR image-reject filter: fs=3072000, pass=100000, stop=484000, decim=4, fout=768000
|
||||
constexpr fir_taps_real<24> taps_200k_wfm_decim_0 = {
|
||||
.pass_frequency_normalized = 100000.0f / 3072000.0f,
|
||||
.stop_frequency_normalized = 484000.0f / 3072000.0f,
|
||||
.low_frequency_normalized = -100000.0f / 3072000.0f,
|
||||
.high_frequency_normalized = 100000.0f / 3072000.0f,
|
||||
.transition_normalized = 384000.0f / 3072000.0f,
|
||||
.taps = { {
|
||||
48, -18, -151, -364, -557, -548, -139, 789,
|
||||
2187, 3800, 5230, 6071, 6071, 5230, 3800, 2187,
|
||||
@@ -353,8 +371,9 @@ constexpr fir_taps_real<24> taps_200k_wfm_decim_0 = {
|
||||
|
||||
// IFIR prototype filter: fs=768000, pass=100000, stop=284000, decim=2, fout=384000
|
||||
constexpr fir_taps_real<16> taps_200k_wfm_decim_1 = {
|
||||
.pass_frequency_normalized = 100000.0f / 768000.0f,
|
||||
.stop_frequency_normalized = 284000.0f / 768000.0f,
|
||||
.low_frequency_normalized = -100000.0f / 768000.0f,
|
||||
.high_frequency_normalized = 100000.0f / 768000.0f,
|
||||
.transition_normalized = 184000.0f / 768000.0f,
|
||||
.taps = { {
|
||||
-67, -123, 388, 622, -1342, -2185, 4599, 14486,
|
||||
14486, 4599, -2185, -1342, 622, 388, -123, -67,
|
||||
@@ -369,8 +388,9 @@ constexpr fir_taps_real<16> taps_200k_wfm_decim_1 = {
|
||||
* sum(abs(taps)): 125270
|
||||
*/
|
||||
constexpr fir_taps_real<64> taps_64_lp_156_198 {
|
||||
.pass_frequency_normalized = 0.156f,
|
||||
.stop_frequency_normalized = 0.196f,
|
||||
.low_frequency_normalized = -0.156f,
|
||||
.high_frequency_normalized = 0.156f,
|
||||
.transition_normalized = 0.04f,
|
||||
.taps = { {
|
||||
-27, 166, 104, -36, -174, -129, 109, 287,
|
||||
148, -232, -430, -130, 427, 597, 49, -716,
|
||||
@@ -387,8 +407,9 @@ constexpr fir_taps_real<64> taps_64_lp_156_198 {
|
||||
|
||||
// IFIR image-reject filter: fs=2457600, pass=100000, stop=407200, decim=4, fout=614400
|
||||
static constexpr fir_taps_real<24> taps_200k_decim_0 = {
|
||||
.pass_frequency_normalized = 100000.0f / 2457600.0f,
|
||||
.stop_frequency_normalized = 407200.0f / 2457600.0f,
|
||||
.low_frequency_normalized = -100000.0f / 2457600.0f,
|
||||
.high_frequency_normalized = 100000.0f / 2457600.0f,
|
||||
.transition_normalized = 307200.0f / 2457600.0f,
|
||||
.taps = { {
|
||||
90, 94, 4, -240, -570, -776, -563, 309,
|
||||
1861, 3808, 5618, 6710, 6710, 5618, 3808, 1861,
|
||||
@@ -398,8 +419,9 @@ static constexpr fir_taps_real<24> taps_200k_decim_0 = {
|
||||
|
||||
// IFIR prototype filter: fs=614400, pass=100000, stop=207200, decim=2, fout=307200
|
||||
static constexpr fir_taps_real<16> taps_200k_decim_1 = {
|
||||
.pass_frequency_normalized = 100000.0f / 614400.0f,
|
||||
.stop_frequency_normalized = 207200.0f / 614400.0f,
|
||||
.low_frequency_normalized = -100000.0f / 614400.0f,
|
||||
.high_frequency_normalized = 100000.0f / 614400.0f,
|
||||
.transition_normalized = 107200.0f / 614400.0f,
|
||||
.taps = { {
|
||||
-132, -256, 545, 834, -1507, -2401, 4666, 14583,
|
||||
14583, 4666, -2401, -1507, 834, 545, -256, -132,
|
||||
|
||||
@@ -298,8 +298,9 @@ public:
|
||||
struct ChannelSpectrum {
|
||||
std::array<uint8_t, 256> db { { 0 } };
|
||||
uint32_t sampling_rate { 0 };
|
||||
uint32_t channel_filter_pass_frequency { 0 };
|
||||
uint32_t channel_filter_stop_frequency { 0 };
|
||||
int32_t channel_filter_low_frequency { 0 };
|
||||
int32_t channel_filter_high_frequency { 0 };
|
||||
int32_t channel_filter_transition { 0 };
|
||||
};
|
||||
|
||||
using ChannelSpectrumFIFO = FIFO<ChannelSpectrum>;
|
||||
|
||||
@@ -63,6 +63,10 @@ using modem_repeat_range_t = range_t<int32_t>;
|
||||
constexpr modem_repeat_range_t modem_repeat_range { 1, 99 };
|
||||
constexpr int32_t modem_repeat_reset_value { 5 };
|
||||
|
||||
using clkout_freq_range_t = range_t<uint32_t>;
|
||||
constexpr clkout_freq_range_t clkout_freq_range { 10, 60000 };
|
||||
constexpr uint32_t clkout_freq_reset_value { 10000 };
|
||||
|
||||
/* struct must pack the same way on M4 and M0 cores. */
|
||||
struct data_t {
|
||||
int64_t tuned_frequency;
|
||||
@@ -193,7 +197,7 @@ void set_serial_format(const serial_format_t new_value) {
|
||||
data->serial_format = new_value;
|
||||
}
|
||||
|
||||
static constexpr uint32_t playdead_magic = 0x88d3bb57;
|
||||
/* static constexpr uint32_t playdead_magic = 0x88d3bb57;
|
||||
|
||||
uint32_t playing_dead() {
|
||||
return data->playing_dead;
|
||||
@@ -216,47 +220,59 @@ uint32_t playdead_sequence() {
|
||||
void set_playdead_sequence(const uint32_t new_value) {
|
||||
data->playdead_sequence = new_value;
|
||||
data->playdead_magic = playdead_magic;
|
||||
} */
|
||||
|
||||
// ui_config is an uint32_t var storing information bitwise
|
||||
// bits 0,1,2 store the backlight timer
|
||||
// bits 31, 30,29,28,27 stores the different single bit configs depicted below
|
||||
// bits on position 4 to 19 (16 bits) store the clkout frequency
|
||||
|
||||
bool clkout_enabled() {
|
||||
return data->ui_config & (1 << 27);
|
||||
}
|
||||
|
||||
bool config_speaker() {
|
||||
return (data->ui_config & 0x10000000UL) ? false : true; // Default true
|
||||
return data->ui_config & (1 << 28);
|
||||
}
|
||||
bool stealth_mode() {
|
||||
return (data->ui_config & 0x20000000UL) ? true : false;
|
||||
}
|
||||
|
||||
void set_config_speaker(bool new_value) {
|
||||
data->ui_config = (data->ui_config & ~0x10000000UL) | (!new_value << 28);
|
||||
}
|
||||
|
||||
void set_stealth_mode(const bool v) {
|
||||
data->ui_config = (data->ui_config & ~0x20000000UL) | (v << 29);
|
||||
}
|
||||
|
||||
bool config_splash() {
|
||||
return (data->ui_config & 0x80000000UL) ? true : false;
|
||||
return data->ui_config & (1 << 29);
|
||||
}
|
||||
|
||||
bool config_login() {
|
||||
return (data->ui_config & 0x40000000UL) ? true : false;
|
||||
return data->ui_config & (1 << 30);
|
||||
}
|
||||
|
||||
bool config_splash() {
|
||||
return data->ui_config & (1 << 31);
|
||||
}
|
||||
|
||||
uint32_t config_backlight_timer() {
|
||||
const uint32_t timer_seconds[8] = { 0, 5, 15, 30, 60, 180, 300, 600 };
|
||||
|
||||
return timer_seconds[data->ui_config & 0x00000007UL];
|
||||
return timer_seconds[data->ui_config & 7]; //first three bits, 8 possible values
|
||||
}
|
||||
|
||||
void set_config_splash(bool v) {
|
||||
data->ui_config = (data->ui_config & ~0x80000000UL) | (v << 31);
|
||||
void set_clkout_enabled(bool v) {
|
||||
data->ui_config = (data->ui_config & ~(1 << 27)) | (v << 27);
|
||||
}
|
||||
|
||||
void set_config_speaker(bool v) {
|
||||
data->ui_config = (data->ui_config & ~(1 << 28)) | (v << 28);
|
||||
}
|
||||
|
||||
void set_stealth_mode(bool v) {
|
||||
data->ui_config = (data->ui_config & ~(1 << 29)) | (v << 29);
|
||||
}
|
||||
|
||||
void set_config_login(bool v) {
|
||||
data->ui_config = (data->ui_config & ~0x40000000UL) | (v << 30);
|
||||
data->ui_config = (data->ui_config & ~(1 << 30)) | (v << 30);
|
||||
}
|
||||
|
||||
void set_config_splash(bool v) {
|
||||
data->ui_config = (data->ui_config & ~(1 << 31)) | (v << 31);
|
||||
}
|
||||
|
||||
void set_config_backlight_timer(uint32_t i) {
|
||||
data->ui_config = (data->ui_config & ~0x00000007UL) | (i & 7);
|
||||
data->ui_config = (data->ui_config & ~7) | (i & 7);
|
||||
}
|
||||
|
||||
/*void set_config_textentry(uint8_t new_value) {
|
||||
@@ -287,5 +303,21 @@ void set_pocsag_ignore_address(uint32_t address) {
|
||||
data->pocsag_ignore_address = address;
|
||||
}
|
||||
|
||||
uint32_t clkout_freq() {
|
||||
uint16_t freq = (data->ui_config & 0x000FFFF0) >> 4;
|
||||
if(freq < clkout_freq_range.minimum || freq > clkout_freq_range.maximum) {
|
||||
data->ui_config = (data->ui_config & ~0x000FFFF0) | clkout_freq_reset_value << 4;
|
||||
return clkout_freq_reset_value;
|
||||
}
|
||||
else {
|
||||
return freq;
|
||||
}
|
||||
}
|
||||
|
||||
void set_clkout_freq(uint32_t freq) {
|
||||
data->ui_config = (data->ui_config & ~0x000FFFF0) | (clkout_freq_range.clip(freq) << 4);
|
||||
}
|
||||
|
||||
|
||||
} /* namespace persistent_memory */
|
||||
} /* namespace portapack */
|
||||
|
||||
@@ -81,7 +81,7 @@ uint32_t config_backlight_timer();
|
||||
|
||||
void set_config_splash(bool v);
|
||||
void set_config_login(bool v);
|
||||
void set_config_speaker(bool new_value);
|
||||
void set_config_speaker(bool v);
|
||||
void set_config_backlight_timer(uint32_t i);
|
||||
|
||||
//uint8_t ui_config_textentry();
|
||||
@@ -93,6 +93,11 @@ void set_pocsag_last_address(uint32_t address);
|
||||
uint32_t pocsag_ignore_address();
|
||||
void set_pocsag_ignore_address(uint32_t address);
|
||||
|
||||
bool clkout_enabled();
|
||||
void set_clkout_enabled(bool v);
|
||||
uint32_t clkout_freq();
|
||||
void set_clkout_freq(uint32_t freq);
|
||||
|
||||
} /* namespace persistent_memory */
|
||||
} /* namespace portapack */
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 180 B |
@@ -33,6 +33,8 @@ pix = im.load()
|
||||
outfile.write(struct.pack('H', im.size[0]))
|
||||
outfile.write(struct.pack('H', im.size[1]))
|
||||
|
||||
print("Generating: \t" + outfile.name + "\n from\t\t" + im.filename + "\n please wait...");
|
||||
|
||||
for y in range (0, im.size[1]):
|
||||
line = ''
|
||||
for x in range (0, im.size[0]):
|
||||
@@ -45,6 +47,8 @@ for y in range (0, im.size[1]):
|
||||
# pixel_lcd = (pix[x, y][0] >> 5) << 5
|
||||
# pixel_lcd |= (pix[x, y][1] >> 5) << 2
|
||||
# pixel_lcd |= (pix[x, y][2] >> 6)
|
||||
line += struct.pack('H', pixel_lcd)
|
||||
outfile.write(line)
|
||||
line += str(struct.pack('H', pixel_lcd))
|
||||
outfile.write(line.encode('utf-8'))
|
||||
print(str(y) + '/' + str(im.size[1]) + '\r', end="")
|
||||
|
||||
print("Ready.");
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
f=2500000,d=WWV/BPM 2 MHz
|
||||
f=5000000,d=WWV/BPM 5 MHz
|
||||
f=10000000,d=WWV/BPM 10 MHz
|
||||
f=15000000,d=WWV/BPM 15 MHz
|
||||
f=20000000,d=WWV 20 MHz
|
||||
f=3300000,d=CHU 3 MHz
|
||||
f=7850000,d=CHU 7 MHz
|
||||
f=14670000,d=CHU 14 MHz
|
||||
f=3810000,d=HD2IOA 3 MHz
|
||||
f=4996000,d=RWM 4 MHz
|
||||
f=9996000,d=RWM 9 MHz
|
||||
f=14996000,d=RWM 14 MHz
|
||||
f=4998000,d=EBC 4 MHz
|
||||
f=15006000,d=EBC 15 MHz
|
||||
@@ -0,0 +1,11 @@
|
||||
# INI,END,DESCRIPTION
|
||||
# (Freq range in MHz, min separation 240MHz)
|
||||
# (Description up to 20 char)
|
||||
# (fields comma delimiter)
|
||||
260,500,315/433 MHz KEYFOBS
|
||||
2320,2560,BL / WIFI 2.4GHz
|
||||
5160,5900,WIFI 5GHz
|
||||
10,7250,FULL RANGE
|
||||
140,380,VHF MICS AND MARINE
|
||||
390,420,RADIOSONDES
|
||||
420,660,UHF MICS
|
||||
Reference in New Issue
Block a user