mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-16 03:22:59 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9e7758718c | |||
| 89b878d3bb | |||
| 56d965f3ae | |||
| fe9f0c228f | |||
| 6562e596e8 | |||
| c27192bdbb | |||
| 927e3b11e7 | |||
| a44a00acf0 |
@@ -118,11 +118,16 @@ set(CSRC
|
||||
usb_serial_endpoints.c
|
||||
usb_serial_device_to_host.c
|
||||
i2c_device_to_host.c
|
||||
hackrf_core_mini.c
|
||||
${HACKRF_PATH}/firmware/common/adc.c
|
||||
${HACKRF_PATH}/firmware/common/usb.c
|
||||
${HACKRF_PATH}/firmware/common/usb_queue.c
|
||||
${HACKRF_PATH}/firmware/hackrf_usb/usb_device.c
|
||||
${HACKRF_PATH}/firmware/common/usb_request.c
|
||||
${HACKRF_PATH}/firmware/common/usb_standard_request.c
|
||||
${HACKRF_PATH}/firmware/libopencm3/lib/lpc43xx/scu.c
|
||||
${HACKRF_PATH}/firmware/common/gpio_lpc.c
|
||||
${HACKRF_PATH}/firmware/common/platform_detect.c
|
||||
${CHIBIOS}/os/various/chprintf.c
|
||||
)
|
||||
|
||||
|
||||
@@ -383,6 +383,11 @@ void set_morsetx_key(bool key_down) {
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
void set_bitstream_config(uint32_t deviation, uint8_t mode) {
|
||||
const StreamTXConfigurationMessage message{deviation, mode};
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
static bool baseband_image_running = false;
|
||||
|
||||
void run_image(const spi_flash::image_tag_t image_tag) {
|
||||
|
||||
@@ -108,6 +108,7 @@ void set_morsetx_key(bool key_down);
|
||||
void set_wefax_config(uint8_t lpm, uint8_t ioc);
|
||||
void set_noaaapt_config();
|
||||
void set_flex_config();
|
||||
void set_bitstream_config(uint32_t deviation, uint8_t mode); // mode 0 for am, 1 for 2fsk
|
||||
|
||||
void request_roger_beep();
|
||||
void request_rssi_beep();
|
||||
|
||||
@@ -80,7 +80,7 @@ bool FlipperTxView::on_file_changed(std::filesystem::path new_file_path) {
|
||||
return false;
|
||||
}
|
||||
preset = submeta.value().preset;
|
||||
if (preset != FLIPPER_PRESET_OOK) {
|
||||
if (preset != FLIPPER_PRESET_OOK && preset != FLIPPER_PRESET_2FSK) {
|
||||
field_filename.set_text("File: err, not supp. preset");
|
||||
return false;
|
||||
}
|
||||
@@ -108,8 +108,13 @@ void FlipperTxView::stop() {
|
||||
bool FlipperTxView::start() {
|
||||
if (filename.empty()) return false;
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
transmitter_model.set_sampling_rate(OOK_SAMPLERATE);
|
||||
transmitter_model.enable();
|
||||
button_startstop.set_text(LanguageHelper::currentMessages[LANG_STOP]);
|
||||
uint8_t mode = 0;
|
||||
if (preset == FLIPPER_PRESET_2FSK)
|
||||
mode = 1;
|
||||
baseband::set_bitstream_config(FM_DEVIATION, mode);
|
||||
// start thread
|
||||
replay_thread = std::make_unique<FlipperPlayThread>(
|
||||
filename,
|
||||
|
||||
@@ -28,6 +28,8 @@ using namespace ui;
|
||||
namespace ui::external_app::flippertx {
|
||||
|
||||
#define OOK_SAMPLERATE 2280000U
|
||||
#define FM_DEVIATION 60000U
|
||||
|
||||
class FlipperPlayThread;
|
||||
class FlipperTxView : public View {
|
||||
public:
|
||||
|
||||
+35
-27
@@ -17,6 +17,7 @@ MorseRadioView::MorseRadioView(ui::NavigationView& nav)
|
||||
&txt_last,
|
||||
&txt_speed,
|
||||
&txt_freq,
|
||||
&txt_clip,
|
||||
&options_mode,
|
||||
&btn_clear,
|
||||
&console_text,
|
||||
@@ -33,41 +34,44 @@ MorseRadioView::MorseRadioView(ui::NavigationView& nav)
|
||||
logger->init_daily_log(logs_dir);
|
||||
};
|
||||
|
||||
audio::output::start();
|
||||
receiver_model.set_sampling_rate(3072000);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
receiver_model.enable();
|
||||
field_squelch.on_change = [this](int32_t v) {
|
||||
receiver_model.set_squelch_level(v);
|
||||
};
|
||||
|
||||
options_mode.on_change = [this](size_t, int32_t value) {
|
||||
current_mode = (uint8_t)value;
|
||||
options_mode.on_change = [this](size_t, int32_t mode) {
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
morse_decoder_.resetLearning();
|
||||
if (current_mode == 0) {
|
||||
if (mode == MORSE_NFM) {
|
||||
receiver_model.set_am_configuration(4);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
|
||||
field_squelch.set_style(Theme::getInstance()->option_active);
|
||||
field_squelch.set_focusable(true);
|
||||
receiver_model.set_squelch_level(field_squelch.value());
|
||||
field_squelch.set_value(receiver_model.squelch_level());
|
||||
audio::set_rate(audio::Rate::Hz_24000);
|
||||
} else {
|
||||
audio::set_rate(audio::Rate::Hz_12000);
|
||||
receiver_model.set_modulation(ReceiverModel::Mode::AMAudio);
|
||||
receiver_model.set_am_configuration(current_mode + 7);
|
||||
receiver_model.set_squelch_level(0);
|
||||
if (mode == MORSE_AM_CW || mode == MORSE_AM_DSB)
|
||||
receiver_model.set_am_configuration(7);
|
||||
else if (mode == MORSE_AM_USB)
|
||||
receiver_model.set_am_configuration(9);
|
||||
else // LSB
|
||||
receiver_model.set_am_configuration(10);
|
||||
field_squelch.set_style(Theme::getInstance()->fg_dark);
|
||||
field_squelch.set_focusable(false);
|
||||
audio::set_rate(audio::Rate::Hz_12000);
|
||||
}
|
||||
baseband::set_moreserx_config(current_mode);
|
||||
};
|
||||
field_squelch.set_value(receiver_model.squelch_level(), false); // will be sent later, no need to send 2x
|
||||
field_squelch.on_change = [this](int32_t v) {
|
||||
if (current_mode == 0)
|
||||
receiver_model.set_squelch_level(v);
|
||||
};
|
||||
options_mode.set_selected_index(current_mode, true);
|
||||
baseband::set_moreserx_config(mode);
|
||||
saved_mode = mode;
|
||||
|
||||
auto vol = field_volume.value(); // audio volume fix
|
||||
field_volume.set_value(0);
|
||||
field_volume.set_value(vol);
|
||||
audio::output::start();
|
||||
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // WM8731 hack.
|
||||
|
||||
receiver_model.set_sampling_rate(3072000);
|
||||
receiver_model.set_baseband_bandwidth(1750000);
|
||||
receiver_model.enable();
|
||||
};
|
||||
options_mode.set_selected_index(saved_mode);
|
||||
|
||||
logger = std::make_unique<MorseLogger>();
|
||||
chk_log.on_select = [this](Checkbox&, bool save) {
|
||||
@@ -103,21 +107,21 @@ void MorseLogger::init_daily_log(const std::filesystem::path& log_dir) {
|
||||
}
|
||||
}
|
||||
|
||||
void MorseLogger::radio_set_log(uint8_t current_mode) {
|
||||
void MorseLogger::radio_set_log(const std::string& morse_mode) {
|
||||
int64_t freq = receiver_model.target_frequency();
|
||||
std::string header = "Freq:" + to_string_rounded_freq(freq, 4);
|
||||
header += "MHz, ";
|
||||
header += "RX MODE:" + std::string(current_mode == 0 ? "CW/FM" : (current_mode == 1 ? "USB" : "LSB"));
|
||||
header += "RX MODE:" + morse_mode;
|
||||
header += "\r\nMessage:";
|
||||
log_file.write_raw(header);
|
||||
}
|
||||
|
||||
bool MorseLogger::on_packet(const std::string& content, bool time, uint8_t current_mode) {
|
||||
bool MorseLogger::on_packet(const std::string& content, bool time, const std::string& morse_mode) {
|
||||
if (!time) {
|
||||
log_file.write_raw_no_newline("\r\n\r\n");
|
||||
auto timestamp = to_string_datetime(rtc_time::now(), YMDHMS);
|
||||
log_file.write_raw("[" + timestamp + "] ");
|
||||
radio_set_log(current_mode);
|
||||
radio_set_log(morse_mode);
|
||||
char_count = 0;
|
||||
time = true;
|
||||
}
|
||||
@@ -152,6 +156,8 @@ MorseRadioView::~MorseRadioView() {
|
||||
|
||||
void MorseRadioView::focus() {
|
||||
field_frequency.focus();
|
||||
txt_clip.set_style(Theme::getInstance()->fg_red);
|
||||
txt_clip.hidden(true);
|
||||
}
|
||||
|
||||
void MorseRadioView::check_for_timeout() {
|
||||
@@ -205,6 +211,8 @@ int32_t MorseRadioView::ProcessSignal(int32_t sig_time_us) {
|
||||
void MorseRadioView::on_data(const MorseRXDataMessage* message) {
|
||||
int32_t r;
|
||||
|
||||
txt_clip.hidden(!message->clipped);
|
||||
|
||||
for (uint8_t i = 0; i <= message->state_cnt; ++i) {
|
||||
r = ProcessSignal(message->state_durations[i]);
|
||||
auto result = morse_decoder_.handleInput((r != 0) ? r : 0);
|
||||
@@ -212,7 +220,7 @@ void MorseRadioView::on_data(const MorseRXDataMessage* message) {
|
||||
last_activity_time = chTimeNow(); // start reset timer on valid input
|
||||
writeCharToConsole(result.text, result.confidence);
|
||||
if (logger && save_log) {
|
||||
time_stamp = logger->on_packet(result.text, time_stamp, current_mode);
|
||||
time_stamp = logger->on_packet(result.text, time_stamp, options_mode.selected_index_name());
|
||||
}
|
||||
float dah_time = morse_decoder_.getCurrentTimeUnit() * 3.0f;
|
||||
if (dah_time > 0) {
|
||||
|
||||
@@ -54,8 +54,8 @@ class MorseLogger {
|
||||
}
|
||||
|
||||
void init_daily_log(const std::filesystem::path& log_dir);
|
||||
bool on_packet(const std::string& content, bool time, uint8_t current_mode);
|
||||
void radio_set_log(uint8_t current_mode);
|
||||
bool on_packet(const std::string& content, bool time, const std::string& morse_mode);
|
||||
void radio_set_log(const std::string& morse_mode);
|
||||
|
||||
private:
|
||||
LogFile log_file{};
|
||||
@@ -83,12 +83,21 @@ class MorseRadioView : public ui::View {
|
||||
RxRadioState radio_state_{};
|
||||
std::unique_ptr<MorseLogger> logger{};
|
||||
|
||||
uint8_t current_mode{0}; // 0=CW/FM, 1=USB, 2=LSB
|
||||
enum morse_modes : uint8_t {
|
||||
MORSE_AM_CW = 0,
|
||||
MORSE_NFM,
|
||||
MORSE_AM_DSB,
|
||||
MORSE_AM_USB,
|
||||
MORSE_AM_LSB,
|
||||
};
|
||||
uint8_t saved_mode = MORSE_AM_CW;
|
||||
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_morese_radio",
|
||||
"rx_morse_radio",
|
||||
app_settings::Mode::RX,
|
||||
{{"cwmode"sv, ¤t_mode}}};
|
||||
{
|
||||
{"cwmode"sv, &saved_mode},
|
||||
}};
|
||||
|
||||
RxFrequencyField field_frequency{
|
||||
{UI_POS_X(0), UI_POS_Y(0)},
|
||||
@@ -115,6 +124,7 @@ class MorseRadioView : public ui::View {
|
||||
ui::Text txt_speed{{UI_POS_X(23), UI_POS_Y(1), UI_POS_WIDTH(3), UI_POS_HEIGHT(1)}, "??"};
|
||||
ui::Text txt_last{{UI_POS_X(12), UI_POS_Y(4), UI_POS_WIDTH_REMAINING(12), UI_POS_HEIGHT(1)}, ""};
|
||||
ui::Text txt_freq{{UI_POS_X(21), UI_POS_Y(2), UI_POS_WIDTH(5), UI_POS_HEIGHT(1)}, "??"};
|
||||
ui::Text txt_clip{{UI_POS_X(15), UI_POS_Y(3), UI_POS_WIDTH(5), UI_POS_HEIGHT(1)}, "clipping"};
|
||||
ui::Console console_text{{UI_POS_X(0), UI_POS_Y(5), UI_POS_MAXWIDTH, UI_POS_HEIGHT_REMAINING(7)}};
|
||||
ui::Labels labels{
|
||||
{{UI_POS_X(0), UI_POS_Y(1)}, "Squelch:", Theme::getInstance()->fg_light->foreground},
|
||||
@@ -125,9 +135,15 @@ class MorseRadioView : public ui::View {
|
||||
{{UI_POS_X(27), UI_POS_Y(2)}, "Hz", Theme::getInstance()->fg_light->foreground}};
|
||||
|
||||
ui::OptionsField options_mode{
|
||||
{UI_POS_X(9), UI_POS_Y(2)},
|
||||
5,
|
||||
{{"CW/FM", 0}, {"USB", 1}, {"LSB", 2}}};
|
||||
{UI_POS_X(8), UI_POS_Y(2) + 4}, // +4 to align with 'Log' checkbox text
|
||||
6,
|
||||
{
|
||||
{"AM/CW", MORSE_AM_CW},
|
||||
{"NFM", MORSE_NFM},
|
||||
{"AM/DSB", MORSE_AM_DSB},
|
||||
{"AM/USB", MORSE_AM_USB},
|
||||
{"AM/LSB", MORSE_AM_LSB},
|
||||
}};
|
||||
|
||||
Checkbox chk_log{{UI_POS_X(0), UI_POS_Y(2)}, 12, "Log", false};
|
||||
ui::Button btn_clear{{UI_POS_X(0), UI_POS_Y_BOTTOM(2), UI_POS_WIDTH(6), UI_POS_HEIGHT(1)}, "CLR"};
|
||||
|
||||
+18
-2
@@ -91,6 +91,7 @@ SubCarView::SubCarView(NavigationView& nav)
|
||||
&button_clear_list,
|
||||
&check_log,
|
||||
&labels,
|
||||
&options_mode,
|
||||
&recent_entries_view});
|
||||
|
||||
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
|
||||
@@ -114,11 +115,17 @@ SubCarView::SubCarView(NavigationView& nav)
|
||||
recent_entries_view.on_select = [this](const SubCarRecentEntry& entry) {
|
||||
nav_.push<SubCarRecentEntryDetailView>(entry);
|
||||
};
|
||||
baseband::set_subghzd_config(0, receiver_model.sampling_rate()); // 0=am
|
||||
receiver_model.enable();
|
||||
|
||||
options_mode.on_change = [this](size_t, int32_t v) {
|
||||
modulation = v;
|
||||
chThdSleepMilliseconds(100); // wait for the baseband thread to process the previous config, to avoid glitchy output when switching modes
|
||||
baseband::set_subghzd_config(modulation, receiver_model.sampling_rate());
|
||||
};
|
||||
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
|
||||
on_tick_second();
|
||||
};
|
||||
options_mode.set_selected_index(modulation, true);
|
||||
receiver_model.enable();
|
||||
}
|
||||
|
||||
void SubCarView::on_tick_second() {
|
||||
@@ -176,6 +183,8 @@ const char* SubCarView::getSensorTypeName(FPROTO_SUBCAR_SENSOR type) {
|
||||
return "Fiat V0";
|
||||
case FPC_BMWV0:
|
||||
return "BMW V0";
|
||||
/* case FPC_KIAV6:
|
||||
return "Kia V6";*/
|
||||
|
||||
case FPC_Invalid:
|
||||
default:
|
||||
@@ -491,6 +500,13 @@ void SubCarRecentEntryDetailView::parseProtocol() {
|
||||
btn = to_string_dec_uint(button);
|
||||
}
|
||||
|
||||
/*if (entry_.sensorType == FPC_KIAV6) {
|
||||
// not decrypted!
|
||||
serial = 0;
|
||||
btn = "?";
|
||||
cnt = 0;
|
||||
}*/
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+19
-13
@@ -116,25 +116,27 @@ class SubCarView : public View {
|
||||
4'000'000 /* sampling rate */,
|
||||
ReceiverModel::Mode::AMAudio};
|
||||
bool logging = false;
|
||||
uint8_t modulation = 0;
|
||||
app_settings::SettingsManager settings_{
|
||||
"rx_subcar",
|
||||
app_settings::Mode::RX,
|
||||
{
|
||||
{"log"sv, &logging},
|
||||
{"modulationmode"sv, &modulation},
|
||||
}};
|
||||
|
||||
SubCarRecentEntries recent{};
|
||||
|
||||
RFAmpField field_rf_amp{
|
||||
{13 * 8, UI_POS_Y(0)}};
|
||||
{UI_POS_X(13), UI_POS_Y(0)}};
|
||||
LNAGainField field_lna{
|
||||
{15 * 8, UI_POS_Y(0)}};
|
||||
{UI_POS_X(15), UI_POS_Y(0)}};
|
||||
VGAGainField field_vga{
|
||||
{18 * 8, UI_POS_Y(0)}};
|
||||
{UI_POS_X(18), UI_POS_Y(0)}};
|
||||
RSSI rssi{
|
||||
{21 * 8, 0, UI_POS_WIDTH_REMAINING(24), 4}};
|
||||
{UI_POS_X(21), 0, UI_POS_WIDTH_REMAINING(24), 4}};
|
||||
Channel channel{
|
||||
{21 * 8, 5, UI_POS_WIDTH_REMAINING(24), 4},
|
||||
{UI_POS_X(21), 5, UI_POS_WIDTH_REMAINING(24), 4},
|
||||
};
|
||||
RxFrequencyField field_frequency{
|
||||
{UI_POS_X(0), UI_POS_Y(0)},
|
||||
@@ -143,18 +145,22 @@ class SubCarView : public View {
|
||||
SignalToken signal_token_tick_second{};
|
||||
|
||||
Button button_clear_list{
|
||||
{0, 16, 7 * 8, 32},
|
||||
{UI_POS_X(0), UI_POS_Y(1), UI_POS_WIDTH(7), UI_POS_HEIGHT(2)},
|
||||
"Clear"};
|
||||
|
||||
Checkbox check_log{
|
||||
{10 * 8, 18},
|
||||
{UI_POS_X(8), UI_POS_Y(1)},
|
||||
3,
|
||||
"Log",
|
||||
true};
|
||||
|
||||
Labels labels{
|
||||
{{UI_POS_X_RIGHT(14), UI_POS_Y(1)}, "no fm yet :(", Theme::getInstance()->fg_light->foreground},
|
||||
{{UI_POS_X(15), UI_POS_Y(1)}, "Mode:", Theme::getInstance()->fg_light->foreground},
|
||||
};
|
||||
ui::OptionsField options_mode{
|
||||
{UI_POS_X(22), UI_POS_Y(1)},
|
||||
3,
|
||||
{{"AM", 0}, {"FM", 1}}};
|
||||
|
||||
static constexpr auto header_height = 3 * 16;
|
||||
|
||||
@@ -198,16 +204,16 @@ class SubCarRecentEntryDetailView : public View {
|
||||
std::string btn = "";
|
||||
uint32_t cnt = SD_NO_CNT;
|
||||
|
||||
Text text_type{{UI_POS_X(0), 1 * 16, 15 * 8, 16}, "?"};
|
||||
Text text_id{{6 * 8, 2 * 16, 10 * 8, 16}, "?"};
|
||||
Text text_type{{UI_POS_X(0), UI_POS_Y(1), UI_POS_WIDTH(15), UI_POS_HEIGHT(1)}, "?"};
|
||||
Text text_id{{UI_POS_X(6), UI_POS_Y(2), UI_POS_WIDTH(10), UI_POS_HEIGHT(1)}, "?"};
|
||||
|
||||
Console console{
|
||||
{0, 4 * 16, screen_width, screen_height - (4 * 16) - 36}};
|
||||
{UI_POS_X(0), UI_POS_Y(4), UI_POS_MAXWIDTH, screen_height - (4 * 16) - 36}};
|
||||
|
||||
Labels labels{
|
||||
{{UI_POS_X(0), UI_POS_Y(0)}, "Type:", Theme::getInstance()->fg_light->foreground},
|
||||
{{UI_POS_X(0), 2 * 16}, "Serial: ", Theme::getInstance()->fg_light->foreground},
|
||||
{{UI_POS_X(0), 3 * 16}, "Data:", Theme::getInstance()->fg_light->foreground},
|
||||
{{UI_POS_X(0), UI_POS_Y(2)}, "Serial: ", Theme::getInstance()->fg_light->foreground},
|
||||
{{UI_POS_X(0), UI_POS_Y(3)}, "Data:", Theme::getInstance()->fg_light->foreground},
|
||||
};
|
||||
|
||||
Button button_done{
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "gpio_lpc.h"
|
||||
|
||||
typedef enum {
|
||||
LED1 = 0,
|
||||
LED2 = 1,
|
||||
LED3 = 2,
|
||||
LED4 = 3,
|
||||
} led_t;
|
||||
|
||||
/* GPIO Output PinMux */
|
||||
static struct gpio_t gpio_led[] = {
|
||||
GPIO(2, 1),
|
||||
GPIO(2, 2),
|
||||
GPIO(2, 8),
|
||||
#ifdef RAD1O
|
||||
GPIO(5, 26),
|
||||
#endif
|
||||
};
|
||||
|
||||
void delay(uint32_t duration) {
|
||||
while (duration--) {
|
||||
/* cannot be optimized out */
|
||||
__asm__ volatile("nop");
|
||||
}
|
||||
}
|
||||
|
||||
void delay_us_at_mhz(uint32_t us, uint32_t mhz) {
|
||||
/* overflow-safe multiply */
|
||||
uint64_t cycles64 = (uint64_t)us * (uint64_t)mhz;
|
||||
if (cycles64 > UINT32_MAX) {
|
||||
cycles64 = UINT32_MAX;
|
||||
}
|
||||
delay((uint32_t)cycles64);
|
||||
}
|
||||
|
||||
void led_on(const led_t led) {
|
||||
gpio_set(&gpio_led[led]);
|
||||
}
|
||||
|
||||
void led_off(const led_t led) {
|
||||
gpio_clear(&gpio_led[led]);
|
||||
}
|
||||
|
||||
void halt_and_flash(const uint32_t duration) {
|
||||
/* blink LED1, LED2, and LED3 */
|
||||
while (1) {
|
||||
led_on(LED1);
|
||||
led_on(LED2);
|
||||
led_on(LED3);
|
||||
delay(duration);
|
||||
led_off(LED1);
|
||||
led_off(LED2);
|
||||
led_off(LED3);
|
||||
delay(duration);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,10 @@
|
||||
#include "chprintf.h"
|
||||
#include "portapack.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "platform_detect.h"
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shell termination event source.
|
||||
*/
|
||||
@@ -65,8 +69,42 @@ static void list_commands(BaseSequentialStream* chp, const ShellCommand* scp) {
|
||||
}
|
||||
}
|
||||
|
||||
static const char* get_board_revision_string(board_rev_t rev) {
|
||||
switch (rev) {
|
||||
case BOARD_REV_HACKRF1_OLD:
|
||||
return "HackRF R1-R5";
|
||||
case BOARD_REV_HACKRF1_R6:
|
||||
return "HackRF R6";
|
||||
case BOARD_REV_HACKRF1_R7:
|
||||
return "HackRF R7";
|
||||
case BOARD_REV_HACKRF1_R8:
|
||||
return "HackRF R8";
|
||||
case BOARD_REV_HACKRF1_R9:
|
||||
return "HackRF R9";
|
||||
case BOARD_REV_HACKRF1_R10:
|
||||
return "HackRF R10";
|
||||
case BOARD_REV_GSG_HACKRF1_R6:
|
||||
return "GSG HackRF R6";
|
||||
case BOARD_REV_GSG_HACKRF1_R7:
|
||||
return "GSG HackRF R7";
|
||||
case BOARD_REV_GSG_HACKRF1_R8:
|
||||
return "GSG HackRF R8";
|
||||
case BOARD_REV_GSG_HACKRF1_R9:
|
||||
return "GSG HackRF R9";
|
||||
case BOARD_REV_GSG_HACKRF1_R10:
|
||||
return "GSG HackRF R10";
|
||||
case BOARD_REV_UNRECOGNIZED:
|
||||
return "Unrecognized";
|
||||
case BOARD_REV_UNDETECTED:
|
||||
return "Undetected";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_info(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
(void)argv;
|
||||
|
||||
if (argc > 0) {
|
||||
usage(chp, const_cast<char*>("info"));
|
||||
return;
|
||||
@@ -92,7 +130,11 @@ static void cmd_info(BaseSequentialStream* chp, int argc, char* argv[]) {
|
||||
#ifdef VERSION_STRING
|
||||
chprintf(chp, "Mayhem Version: %s\r\n", VERSION_STRING);
|
||||
#endif
|
||||
chprintf(chp, "HackRF Board Rev: %s\r\n", hackrf_r9 ? "R9" : "R1-R8");
|
||||
|
||||
detect_hardware_platform();
|
||||
board_rev_t revision = detected_revision();
|
||||
const char* revision_string = get_board_revision_string(revision);
|
||||
chprintf(chp, "HackRF Board Rev: %s\r\n", revision_string);
|
||||
chprintf(chp, "Reference Source: %s\r\n", portapack::clock_manager.get_source().c_str());
|
||||
chprintf(chp, "Reference Freq: %s\r\n", portapack::clock_manager.get_freq().c_str());
|
||||
#ifdef __DATE__
|
||||
|
||||
@@ -657,7 +657,13 @@ void GeoMap::move(const float lon, const float lat) {
|
||||
} else {
|
||||
if (is_changed) {
|
||||
set_osm_max_zoom();
|
||||
redraw_map = true;
|
||||
double global_center_px = lon_to_pixel_x_tile(lon_, map_osm_real_zoom);
|
||||
double global_center_py = lat_to_pixel_y_tile(lat_, map_osm_real_zoom);
|
||||
// Redraw if viewport moved by at least 1 pixel (includes zoom level changes)
|
||||
if (abs(global_center_px - (r.width() / 2.0) - viewport_top_left_px) >= 1.0 ||
|
||||
abs(global_center_py - (r.height() / 2.0) - viewport_top_left_py) >= 1.0) {
|
||||
redraw_map = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -618,12 +618,12 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PADT adsbtx)
|
||||
|
||||
### OOKStream
|
||||
### Binary Timed stream TX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_ook_stream_tx.cpp
|
||||
proc_bint_stream_tx.cpp
|
||||
)
|
||||
DeclareTargets(POSK ookstream)
|
||||
DeclareTargets(POSK bintstream)
|
||||
|
||||
|
||||
### Signal generator
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
#pragma once
|
||||
#include "subcarbase.hpp"
|
||||
#include <cstring>
|
||||
|
||||
typedef enum {
|
||||
KiaV6DecoderStepReset = 0,
|
||||
KiaV6DecoderStepWaitFirstHigh,
|
||||
KiaV6DecoderStepCountPreamble,
|
||||
KiaV6DecoderStepWaitLongHigh,
|
||||
KiaV6DecoderStepData,
|
||||
} KiaV6DecoderStep;
|
||||
|
||||
#define KIA_V6_XOR_MASK_LOW 0x84AF25FB
|
||||
#define KIA_V6_XOR_MASK_HIGH 0x638766AB
|
||||
|
||||
class FProtoSubCarKiaV6 : public FProtoSubCarBase {
|
||||
public:
|
||||
FProtoSubCarKiaV6() {
|
||||
sensorType = FPC_KIAV6;
|
||||
te_short = 200;
|
||||
te_long = 400;
|
||||
te_delta = 100;
|
||||
min_count_bit_for_found = 144;
|
||||
}
|
||||
|
||||
void feed(bool level, uint32_t duration) {
|
||||
uint32_t uVar4, uVar5;
|
||||
ManchesterEvent event;
|
||||
bool data_bit;
|
||||
uint8_t bit_count_inc;
|
||||
uint32_t step_value;
|
||||
|
||||
switch (parser_step) {
|
||||
case KiaV6DecoderStepReset: // case 0
|
||||
if (level == 0) {
|
||||
return;
|
||||
}
|
||||
if (DURATION_DIFF(duration, te_short) <
|
||||
te_delta) {
|
||||
parser_step = KiaV6DecoderStepWaitFirstHigh;
|
||||
te_last = duration;
|
||||
header_count = 0;
|
||||
FProtoGeneral::manchester_advance(
|
||||
manchester_state,
|
||||
ManchesterEventReset,
|
||||
&manchester_state,
|
||||
NULL);
|
||||
}
|
||||
return;
|
||||
|
||||
case KiaV6DecoderStepWaitFirstHigh: { // case 1
|
||||
if (level != 0) {
|
||||
return;
|
||||
}
|
||||
uint32_t diff_short = DURATION_DIFF(duration, te_short);
|
||||
uint32_t diff_long = DURATION_DIFF(duration, te_long);
|
||||
|
||||
uint32_t diff = (diff_long < diff_short) ? diff_long : diff_short;
|
||||
|
||||
if (diff_long < te_delta && diff_long < diff_short) {
|
||||
if (header_count >= 0x259) { // 601 decimal
|
||||
header_count = 0;
|
||||
te_last = duration;
|
||||
parser_step = KiaV6DecoderStepWaitLongHigh;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (diff >= te_delta) {
|
||||
step_value = KiaV6DecoderStepReset;
|
||||
goto LAB_reset;
|
||||
}
|
||||
|
||||
if (DURATION_DIFF(te_last, te_short) <
|
||||
te_delta) {
|
||||
te_last = duration;
|
||||
header_count++;
|
||||
return;
|
||||
} else {
|
||||
step_value = KiaV6DecoderStepReset;
|
||||
goto LAB_reset;
|
||||
}
|
||||
}
|
||||
case KiaV6DecoderStepWaitLongHigh: { // case 2
|
||||
if (level == 0) {
|
||||
step_value = KiaV6DecoderStepReset;
|
||||
goto LAB_reset;
|
||||
}
|
||||
uint32_t diff_long_check = DURATION_DIFF(duration, te_long);
|
||||
uint32_t diff_short_check = DURATION_DIFF(duration, te_short);
|
||||
|
||||
if (diff_long_check >= te_delta) {
|
||||
if (diff_short_check >= te_delta) {
|
||||
step_value = KiaV6DecoderStepReset;
|
||||
goto LAB_reset;
|
||||
}
|
||||
}
|
||||
|
||||
if (DURATION_DIFF(te_last, te_long) >=
|
||||
te_delta) {
|
||||
step_value = KiaV6DecoderStepReset;
|
||||
goto LAB_reset;
|
||||
}
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
subghz_protocol_blocks_add_bit(0);
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
|
||||
data_part1_low = (uint32_t)(decode_data & 0xFFFFFFFF);
|
||||
data_part1_high = (uint32_t)((decode_data >> 32) & 0xFFFFFFFF);
|
||||
bit_count = decode_count_bit;
|
||||
|
||||
parser_step = KiaV6DecoderStepData;
|
||||
return;
|
||||
}
|
||||
case KiaV6DecoderStepData: // case 3
|
||||
if (DURATION_DIFF(duration, te_short) <
|
||||
te_delta) {
|
||||
event = (ManchesterEvent)((level & 0x7F) << 1);
|
||||
goto manchester_process;
|
||||
} else if (
|
||||
DURATION_DIFF(duration, te_long) <
|
||||
te_delta) {
|
||||
event = (ManchesterEvent)(level ? 6 : 4);
|
||||
goto manchester_process;
|
||||
}
|
||||
step_value = KiaV6DecoderStepReset;
|
||||
goto LAB_reset;
|
||||
|
||||
manchester_process:
|
||||
if (FProtoGeneral::manchester_advance(
|
||||
manchester_state, event, &manchester_state, &data_bit)) {
|
||||
uVar4 = data_part1_low;
|
||||
uVar5 = (uVar4 << 1) | (data_bit ? 1 : 0);
|
||||
|
||||
uint32_t carry = (uVar4 >> 31) & 1;
|
||||
uVar4 = (data_part1_high << 1) | carry;
|
||||
|
||||
data_part1_low = uVar5;
|
||||
data_part1_high = uVar4;
|
||||
|
||||
decode_data = ((uint64_t)uVar4 << 32) | uVar5;
|
||||
|
||||
bit_count_inc = bit_count + 1;
|
||||
bit_count = bit_count_inc;
|
||||
|
||||
if (bit_count_inc == 0x40) {
|
||||
// stored_part1_low = ~uVar5;
|
||||
// stored_part1_high = ~uVar4;
|
||||
data_part1_low = 0;
|
||||
data_part1_high = 0;
|
||||
} else if (bit_count_inc == 0x80) {
|
||||
// stored_part2_low = ~uVar5;
|
||||
// stored_part2_high = ~uVar4;
|
||||
data_part1_low = 0;
|
||||
data_part1_high = 0;
|
||||
}
|
||||
}
|
||||
|
||||
te_last = duration;
|
||||
|
||||
if (bit_count != min_count_bit_for_found) {
|
||||
return;
|
||||
}
|
||||
data_count_bit = min_count_bit_for_found;
|
||||
// data_part3 = ~((uint16_t)data_part1_low);
|
||||
|
||||
// kia_v6_decrypt(); --won't
|
||||
decode_data = data_part1_low | ((uint64_t)data_part1_high << 32);
|
||||
if (callback) {
|
||||
callback(this);
|
||||
}
|
||||
|
||||
data_part1_low = 0;
|
||||
data_part1_high = 0;
|
||||
bit_count = 0;
|
||||
step_value = KiaV6DecoderStepReset;
|
||||
goto LAB_reset;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
LAB_reset:
|
||||
parser_step = step_value;
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t bit_count = 0;
|
||||
|
||||
uint16_t header_count = 0;
|
||||
ManchesterState manchester_state = ManchesterStateMid1;
|
||||
uint32_t data_part1_low = 0;
|
||||
uint32_t data_part1_high = 0;
|
||||
|
||||
// uint32_t stored_part1_low = 0;
|
||||
// uint32_t stored_part1_high = 0;
|
||||
// uint32_t stored_part2_low = 0;
|
||||
// uint32_t stored_part2_high = 0;
|
||||
// uint16_t data_part3 = 0;
|
||||
};
|
||||
@@ -76,7 +76,7 @@ class FProtoSubGhzDSecPlusV1 : public FProtoSubGhzDBase {
|
||||
packet_accepted |= SECPLUS_V1_PACKET_2_ACCEPTED;
|
||||
|
||||
if (packet_accepted == (SECPLUS_V1_PACKET_1_ACCEPTED | SECPLUS_V1_PACKET_2_ACCEPTED)) {
|
||||
// subghz_protocol_secplus_v1_decode(); // disabled doe to lack of flash
|
||||
// subghz_protocol_secplus_v1_decode(); // disabled due to lack of flash
|
||||
// controller
|
||||
// uint32_t fixed = (data >> 32) & 0xFFFFFFFF;
|
||||
// cnt = data & 0xFFFFFFFF;
|
||||
|
||||
@@ -21,6 +21,7 @@ So include here the .hpp, and add a new element to the protos vector in the cons
|
||||
#include "c-ford_v0.hpp"
|
||||
#include "c-fiat_v0.hpp"
|
||||
#include "c-bmw_v0.hpp"
|
||||
// #include "c-kia_v6.hpp"
|
||||
|
||||
#ifndef __FPROTO_PROTOLISTCAR_H__
|
||||
#define __FPROTO_PROTOLISTCAR_H__
|
||||
@@ -42,6 +43,7 @@ class SubCarProtos : public FProtoListGeneral {
|
||||
protos[FPC_FORDV0] = new FProtoSubCarFordV0();
|
||||
protos[FPC_FIATV0] = new FProtoSubCarFiatV0();
|
||||
protos[FPC_BMWV0] = new FProtoSubCarBMWV0();
|
||||
// protos[FPC_KIAV6] = new FProtoSubCarKiaV6(); //-- disabled, due to whole encrypted.
|
||||
|
||||
for (uint8_t i = 0; i < FPC_COUNT; ++i) {
|
||||
if (protos[i] != NULL) protos[i]->setCallback(callbackTarget);
|
||||
|
||||
@@ -8,9 +8,6 @@ These values must be present on the protocol's constructor, like FProtoWeatherAc
|
||||
Also it must have a switch-case element in the getSubGhzDSensorTypeName() function, to display it's name.
|
||||
*/
|
||||
|
||||
#define FPM_AM 0
|
||||
#define FPM_FM 1
|
||||
|
||||
enum FPROTO_SUBCAR_SENSOR : uint8_t {
|
||||
FPC_Invalid = 0,
|
||||
FPC_SUZUKI = 1,
|
||||
@@ -24,6 +21,8 @@ enum FPROTO_SUBCAR_SENSOR : uint8_t {
|
||||
FPC_FORDV0 = 9,
|
||||
FPC_FIATV0 = 10,
|
||||
FPC_BMWV0 = 11,
|
||||
// FPC_KIAV6 = 12, //disabled, due to whole encrypted.
|
||||
// FPC_PSA = 13, // IS whole encrypted
|
||||
FPC_COUNT
|
||||
};
|
||||
|
||||
|
||||
+33
-14
@@ -19,7 +19,7 @@
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "proc_ook_stream_tx.hpp"
|
||||
#include "proc_bint_stream_tx.hpp"
|
||||
#include "sine_table_int8.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
|
||||
@@ -27,27 +27,35 @@
|
||||
|
||||
#include "utility.hpp"
|
||||
|
||||
OOKProcessorStreamed::OOKProcessorStreamed() {
|
||||
BinaryTimedProcessorStreamed::BinaryTimedProcessorStreamed() {
|
||||
configured = false;
|
||||
baseband_thread.start();
|
||||
}
|
||||
|
||||
inline void OOKProcessorStreamed::write_sample(const buffer_c8_t& buffer, bool bit_value, size_t i) {
|
||||
int8_t re, im;
|
||||
|
||||
if (bit_value) {
|
||||
phase = (phase + 200); // What ?
|
||||
inline void BinaryTimedProcessorStreamed::write_sample(const buffer_c8_t& buffer, bool bit_value, size_t i) {
|
||||
int8_t re = 0, im = 0;
|
||||
if (mode == 0) {
|
||||
if (bit_value) {
|
||||
phase = (phase + 200);
|
||||
sphase = phase + (64 << 18);
|
||||
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
|
||||
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
|
||||
}
|
||||
} else if (mode == 1) {
|
||||
// calculate the re, im based on the deviation uint32_t variable to get the re, im, to send out 2fsk signal. based on the bit_value
|
||||
if (bit_value) {
|
||||
phase += deviation_delta;
|
||||
} else {
|
||||
phase -= deviation_delta;
|
||||
}
|
||||
sphase = phase + (64 << 18);
|
||||
re = (sine_table_i8[(sphase & 0x03FC0000) >> 18]);
|
||||
im = (sine_table_i8[(phase & 0x03FC0000) >> 18]);
|
||||
} else {
|
||||
re = 0;
|
||||
im = 0;
|
||||
}
|
||||
buffer.p[i] = {re, im};
|
||||
}
|
||||
|
||||
void OOKProcessorStreamed::execute(const buffer_c8_t& buffer) {
|
||||
void BinaryTimedProcessorStreamed::execute(const buffer_c8_t& buffer) {
|
||||
if (!configured || !stream) return;
|
||||
|
||||
for (size_t i = 0; i < buffer.count; i++) {
|
||||
@@ -87,7 +95,7 @@ void OOKProcessorStreamed::execute(const buffer_c8_t& buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
void OOKProcessorStreamed::on_message(const Message* const message) {
|
||||
void BinaryTimedProcessorStreamed::on_message(const Message* const message) {
|
||||
switch (message->id) {
|
||||
case Message::ID::ReplayConfig:
|
||||
configured = false;
|
||||
@@ -100,12 +108,23 @@ void OOKProcessorStreamed::on_message(const Message* const message) {
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
break;
|
||||
|
||||
case Message::ID::StreamTXConfiguration:
|
||||
streamtx_config(*reinterpret_cast<const StreamTXConfigurationMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OOKProcessorStreamed::replay_config(const ReplayConfigMessage& message) {
|
||||
void BinaryTimedProcessorStreamed::streamtx_config(const StreamTXConfigurationMessage& message) {
|
||||
mode = message.mode;
|
||||
deviation = message.deviation;
|
||||
uint64_t big_calc = (uint64_t)message.deviation << 26;
|
||||
deviation_delta = (uint32_t)(big_calc / OOK_SAMPLERATE);
|
||||
}
|
||||
|
||||
void BinaryTimedProcessorStreamed::replay_config(const ReplayConfigMessage& message) {
|
||||
if (message.config) {
|
||||
txprogress_message.progress = -2;
|
||||
shared_memory.application_queue.push(txprogress_message);
|
||||
@@ -121,7 +140,7 @@ void OOKProcessorStreamed::replay_config(const ReplayConfigMessage& message) {
|
||||
}
|
||||
|
||||
int main() {
|
||||
EventDispatcher event_dispatcher{std::make_unique<OOKProcessorStreamed>()};
|
||||
EventDispatcher event_dispatcher{std::make_unique<BinaryTimedProcessorStreamed>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
||||
+8
-2
@@ -34,10 +34,11 @@
|
||||
#include <memory>
|
||||
|
||||
#define OOK_SAMPLERATE 2280000U
|
||||
#define FM_DEVIATION 60000U
|
||||
|
||||
class OOKProcessorStreamed : public BasebandProcessor {
|
||||
class BinaryTimedProcessorStreamed : public BasebandProcessor {
|
||||
public:
|
||||
OOKProcessorStreamed();
|
||||
BinaryTimedProcessorStreamed();
|
||||
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
void on_message(const Message* const message) override;
|
||||
@@ -53,6 +54,11 @@ class OOKProcessorStreamed : public BasebandProcessor {
|
||||
std::unique_ptr<StreamOutput> stream{};
|
||||
bool configured{false};
|
||||
void replay_config(const ReplayConfigMessage& message);
|
||||
void streamtx_config(const StreamTXConfigurationMessage& message);
|
||||
|
||||
uint8_t mode = 0; // am = 0, 2fsk = 1
|
||||
uint32_t deviation = FM_DEVIATION; // used in 2fsk
|
||||
uint32_t deviation_delta = (FM_DEVIATION * 4294967296ULL) / OOK_SAMPLERATE;
|
||||
|
||||
int32_t endsignals[3] = {0, 42069, 613379}; // 0 is skipped, count from 1, don't ask...
|
||||
uint8_t readerrs = 0; // to count in the array
|
||||
@@ -28,26 +28,32 @@
|
||||
void MorseProcessor::configure(uint8_t mode) {
|
||||
configured = false;
|
||||
|
||||
if (mode == 0) { // CW/FM
|
||||
modulation = static_cast<ModulationMode>((uint8_t)mode);
|
||||
|
||||
if (modulation == ModulationMode::FM) { // FM
|
||||
decim_0.configure(taps_11k0_decim_0.taps);
|
||||
decim_1.configure(taps_11k0_decim_1.taps);
|
||||
channel_filter.configure(taps_11k0_channel.taps, 2);
|
||||
demod_cw_fm.configure(24000, 5000);
|
||||
} else { // USB, LSB
|
||||
decim_0.configure(taps_6k0_decim_0.taps);
|
||||
decim_1.configure(taps_6k0_decim_1.taps);
|
||||
|
||||
if (mode == 1) // USB
|
||||
channel_filter.configure(taps_2k8_usb_channel.taps, 4);
|
||||
else // LSB
|
||||
channel_filter.configure(taps_2k8_lsb_channel.taps, 4);
|
||||
} else {
|
||||
decim_0.configure(taps_4k25_decim_0.taps);
|
||||
decim_1.configure(taps_4k25_decim_1.taps);
|
||||
if (modulation == ModulationMode::AM) { // AM
|
||||
channel_filter.configure(taps_2k0_am_lpf_channel.taps, 4);
|
||||
} else { // SSB, DSB
|
||||
if (modulation == ModulationMode::DSB) // DSB
|
||||
channel_filter.configure(taps_1k5_dsb_lpf.taps, 4);
|
||||
else if (modulation == ModulationMode::USB) // USB
|
||||
channel_filter.configure(taps_1k5_USB_channel.taps, 4);
|
||||
else // LSB
|
||||
channel_filter.configure(taps_1k5_LSB_channel.taps, 4);
|
||||
}
|
||||
}
|
||||
|
||||
modulation = mode;
|
||||
if (mode > 0)
|
||||
audio_output.configure(audio_12k_hpf_300hz_config);
|
||||
else
|
||||
if (modulation == ModulationMode::FM)
|
||||
audio_output.configure(iir_config_passthrough, iir_config_passthrough, (float)user_squelch_level / 100.0f);
|
||||
else
|
||||
audio_output.configure(audio_12k_hpf_300hz_config);
|
||||
|
||||
meas_samples_in_period = 0;
|
||||
meas_last_period_len = 0;
|
||||
@@ -70,6 +76,8 @@ void MorseProcessor::configure(uint8_t mode) {
|
||||
squelch_is_open = true;
|
||||
squelch_hold = 0;
|
||||
|
||||
dc_average = 0.0f;
|
||||
|
||||
current_freq = 700.0f;
|
||||
update_goertzel_coeff(current_freq);
|
||||
|
||||
@@ -77,10 +85,15 @@ void MorseProcessor::configure(uint8_t mode) {
|
||||
}
|
||||
|
||||
inline buffer_f32_t MorseProcessor::demodulate(const buffer_c16_t& channel) {
|
||||
if (modulation > 0) {
|
||||
// SSB always keeps squelch "technically" open for the demodulator
|
||||
// AM,SSB,DSB always keeps squelch "technically" open for the demodulator
|
||||
if (modulation == ModulationMode::AM || modulation == ModulationMode::DSB) {
|
||||
squelch_is_open = true;
|
||||
return demod_ssb.execute(channel, audio_buffer);
|
||||
return demod_AM.execute(channel, audio_buffer);
|
||||
} else {
|
||||
if (modulation == ModulationMode::USB || modulation == ModulationMode::LSB) {
|
||||
squelch_is_open = true;
|
||||
return demod_ssb.execute(channel, audio_buffer);
|
||||
}
|
||||
}
|
||||
return demod_cw_fm.execute(channel, audio_buffer);
|
||||
}
|
||||
@@ -90,7 +103,7 @@ void MorseProcessor::update_goertzel_coeff(float freq) {
|
||||
if (freq < 300.0f) freq = 300.0f;
|
||||
if (freq > 2300.0f) freq = 2300.0f;
|
||||
|
||||
float sample_rate = (modulation == 0) ? 24000.0f : 12000.0f;
|
||||
float sample_rate = (modulation == ModulationMode::FM) ? 24000.0f : 12000.0f;
|
||||
float omega = 2.0f * M_PI * freq / sample_rate;
|
||||
float omega_sq = omega * omega;
|
||||
float cos_approx = 1.0f - (omega_sq * 0.5f);
|
||||
@@ -100,7 +113,7 @@ void MorseProcessor::update_goertzel_coeff(float freq) {
|
||||
|
||||
void MorseProcessor::measure_frequency(int32_t sample) {
|
||||
// Noise gate threshold
|
||||
const int32_t gate_threshold = (modulation == 0) ? 4000 : 2000;
|
||||
const int32_t gate_threshold = (modulation == ModulationMode::FM) ? 4000 : 2000;
|
||||
|
||||
if (sample > gate_threshold || sample < -gate_threshold) {
|
||||
if (sample > 0 && !meas_signal_state_high) {
|
||||
@@ -111,7 +124,7 @@ void MorseProcessor::measure_frequency(int32_t sample) {
|
||||
bool period_is_stable = false;
|
||||
if (meas_last_period_len > 0) {
|
||||
int32_t diff = std::abs((int)meas_samples_in_period - (int)meas_last_period_len);
|
||||
if (diff <= 1) {
|
||||
if (diff <= 2) {
|
||||
meas_consistency_count++;
|
||||
period_is_stable = true;
|
||||
} else {
|
||||
@@ -121,10 +134,12 @@ void MorseProcessor::measure_frequency(int32_t sample) {
|
||||
meas_last_period_len = meas_samples_in_period;
|
||||
|
||||
// Wait for AT LEAST 3 STABLE CYCLES
|
||||
if (period_is_stable && meas_consistency_count > 3) {
|
||||
float base_rate = (modulation == 0) ? 24000.0f : 12000.0f;
|
||||
if (period_is_stable && meas_consistency_count > 5) {
|
||||
float base_rate = (modulation == ModulationMode::FM) ? 24000.0f : 12000.0f;
|
||||
float inst_freq = base_rate / (float)meas_samples_in_period;
|
||||
|
||||
if (modulation == ModulationMode::DSB) {
|
||||
inst_freq /= 2.0f;
|
||||
}
|
||||
// Check for overflows
|
||||
if (inst_freq > 250 && inst_freq < 3000) {
|
||||
meas_freq_accumulator += inst_freq;
|
||||
@@ -148,7 +163,7 @@ void MorseProcessor::measure_frequency(int32_t sample) {
|
||||
meas_samples_in_period++;
|
||||
|
||||
ui_update_timer++;
|
||||
uint32_t update_limit = (modulation == 0) ? 4800 : 2400; // ~200ms
|
||||
uint32_t update_limit = (modulation == ModulationMode::FM) ? 4800 : 2400; // ~200ms
|
||||
|
||||
if (ui_update_timer > update_limit) {
|
||||
if (meas_freq_count > 0) {
|
||||
@@ -202,7 +217,7 @@ void MorseProcessor::process_decoding(int32_t sample) {
|
||||
|
||||
// Tone Detection
|
||||
bool is_tone = squelch_is_open && (power > current_pwr_threshold) && (power > 150000);
|
||||
int32_t time_base = modulation == 0 ? 125 : 250;
|
||||
int32_t time_base = 250;
|
||||
// State Change Logic
|
||||
if (is_tone != was_signaling) {
|
||||
int32_t duration_us = (int32_t)((int64_t)duration_samples * time_base / 3);
|
||||
@@ -254,33 +269,43 @@ void MorseProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
if (raw_int_abs > audio_threshold || user_squelch_level == 0) {
|
||||
squelch_is_open = true;
|
||||
squelch_hold = (modulation == 0) ? 2400 : 1200;
|
||||
squelch_hold = (modulation == ModulationMode::FM) ? 2400 : 1200;
|
||||
} else {
|
||||
if (squelch_hold > 0)
|
||||
squelch_hold--;
|
||||
else if (modulation == 0)
|
||||
else if (modulation == ModulationMode::FM)
|
||||
squelch_is_open = false;
|
||||
}
|
||||
|
||||
measure_frequency((int32_t)(raw_audio * 32768.0f));
|
||||
|
||||
float decode_audio = raw_audio;
|
||||
if (modulation > 0) {
|
||||
const float gain = 6.0f;
|
||||
if (modulation != ModulationMode::FM) {
|
||||
float gain = 16.0f;
|
||||
if (modulation == ModulationMode::USB || modulation == ModulationMode::LSB)
|
||||
gain = 5.0f;
|
||||
decode_audio *= gain;
|
||||
|
||||
// Hard Limiting / Clipping
|
||||
if (decode_audio > 1.0f)
|
||||
message.clipped = false;
|
||||
if (decode_audio > 1.0f) {
|
||||
decode_audio = 1.0f;
|
||||
else if (decode_audio < -1.0f)
|
||||
message.clipped = true;
|
||||
} else if (decode_audio < -1.0f) {
|
||||
decode_audio = -1.0f;
|
||||
|
||||
}
|
||||
audio_buf.p[i] = decode_audio;
|
||||
}
|
||||
|
||||
// DC BLOCKING
|
||||
if (modulation != ModulationMode::FM) {
|
||||
dc_average = (dc_average * 0.95f) + (decode_audio * 0.05f);
|
||||
measure_frequency((int32_t)((decode_audio - dc_average) * 32768.0f));
|
||||
} else {
|
||||
measure_frequency((int32_t)(raw_audio * 32768.0f));
|
||||
}
|
||||
|
||||
process_decoding((int32_t)(decode_audio * 32768.0f));
|
||||
|
||||
if (modulation == 0 && !squelch_is_open) {
|
||||
if (modulation == ModulationMode::FM && !squelch_is_open) {
|
||||
audio_buf.p[i] = 0.0f; // mute nfm on squelch
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,13 +60,22 @@ class MorseProcessor : public BasebandProcessor {
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{};
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1{};
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter{};
|
||||
dsp::demodulate::AM demod_AM{};
|
||||
dsp::demodulate::FM demod_cw_fm{};
|
||||
dsp::demodulate::SSB demod_ssb{};
|
||||
AudioOutput audio_output{};
|
||||
|
||||
bool configured{false};
|
||||
|
||||
uint8_t modulation{0}; // 0=CW/FM, 1=USB, 2=LSB
|
||||
enum class ModulationMode : uint8_t {
|
||||
AM = 0,
|
||||
FM = 1,
|
||||
DSB = 2,
|
||||
USB = 3,
|
||||
LSB = 4
|
||||
};
|
||||
|
||||
ModulationMode modulation = ModulationMode::AM;
|
||||
int32_t user_squelch_level{0};
|
||||
bool squelch_is_open{true};
|
||||
int32_t squelch_hold{0};
|
||||
@@ -79,7 +88,6 @@ class MorseProcessor : public BasebandProcessor {
|
||||
float meas_freq_accumulator{0.0f};
|
||||
uint32_t meas_freq_count{0};
|
||||
uint32_t ui_update_timer{0};
|
||||
|
||||
float current_freq{700.0f};
|
||||
|
||||
// --- Decoding variables (Goertzel) ---
|
||||
@@ -92,6 +100,9 @@ class MorseProcessor : public BasebandProcessor {
|
||||
int64_t noise_floor{5000};
|
||||
int32_t startup_delay{20};
|
||||
|
||||
float dc_average = 0.0f; // DC level tracking
|
||||
int32_t dc_average_int = 0;
|
||||
|
||||
MorseRXDataMessage message{};
|
||||
MorseRXfreqMessage freq_message{};
|
||||
};
|
||||
|
||||
@@ -25,14 +25,6 @@
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
static inline int get_quadrant(int16_t i, int16_t q) {
|
||||
if (i >= 0) {
|
||||
return (q >= 0) ? 0 : 3;
|
||||
} else {
|
||||
return (q >= 0) ? 1 : 2;
|
||||
}
|
||||
}
|
||||
|
||||
void SubCarProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (!configured) return;
|
||||
|
||||
@@ -46,10 +38,6 @@ void SubCarProcessor::execute(const buffer_c8_t& buffer) {
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // Input:512 complex/2 (decim factor) = 256_output complex ( 512 I/Q samples)
|
||||
feed_channel_stats(decim_1_out);
|
||||
|
||||
// for fm
|
||||
const int32_t DC_ALPHA = 5; // Auto-centering speed
|
||||
int32_t buffer_rotation_sum = 0;
|
||||
|
||||
for (size_t i = 0; i < decim_1_out.count; i++) {
|
||||
// am
|
||||
threshold = (low_estimate + high_estimate) / 2;
|
||||
@@ -57,126 +45,103 @@ void SubCarProcessor::execute(const buffer_c8_t& buffer) {
|
||||
int16_t re = decim_1_out.p[i].real();
|
||||
int16_t im = decim_1_out.p[i].imag();
|
||||
uint32_t mag = ((uint32_t)re * (uint32_t)re) + ((uint32_t)im * (uint32_t)im);
|
||||
|
||||
mag = (mag >> 10);
|
||||
int32_t const ook_low_delta = mag - low_estimate;
|
||||
bool meashl = currentHiLow;
|
||||
if (sig_state == STATE_IDLE) {
|
||||
if (mag > (threshold + hysteresis)) { // just become high
|
||||
meashl = true;
|
||||
sig_state = STATE_PULSE;
|
||||
numg = 0;
|
||||
} else {
|
||||
meashl = false; // still low
|
||||
low_estimate += ook_low_delta / OOK_EST_LOW_RATIO;
|
||||
low_estimate += ((ook_low_delta > 0) ? 1 : -1); // Hack to compensate for lack of fixed-point scaling
|
||||
// Calculate default OOK high level estimate
|
||||
high_estimate = 1.35 * low_estimate; // Default is a ratio of low level
|
||||
high_estimate = std::max(high_estimate, min_high_level);
|
||||
high_estimate = std::min(high_estimate, (uint32_t)OOK_MAX_HIGH_LEVEL);
|
||||
}
|
||||
|
||||
} else if (sig_state == STATE_PULSE) {
|
||||
++numg;
|
||||
if (numg > 100) numg = 100;
|
||||
if (mag < (threshold - hysteresis)) {
|
||||
// check if really a bad value
|
||||
if (numg < 3) {
|
||||
// susp
|
||||
sig_state = STATE_GAP;
|
||||
} else {
|
||||
if (modulation == 0) {
|
||||
int32_t const ook_low_delta = mag - low_estimate;
|
||||
bool meashl = currentHiLow;
|
||||
if (sig_state == STATE_IDLE) {
|
||||
if (mag > (threshold + hysteresis)) { // just become high
|
||||
meashl = true;
|
||||
sig_state = STATE_PULSE;
|
||||
numg = 0;
|
||||
sig_state = STATE_GAP_START;
|
||||
} else {
|
||||
meashl = false; // still low
|
||||
low_estimate += ook_low_delta / OOK_EST_LOW_RATIO;
|
||||
low_estimate += ((ook_low_delta > 0) ? 1 : -1); // Hack to compensate for lack of fixed-point scaling
|
||||
// Calculate default OOK high level estimate
|
||||
high_estimate = 1.35 * low_estimate; // Default is a ratio of low level
|
||||
high_estimate = std::max(high_estimate, min_high_level);
|
||||
high_estimate = std::min(high_estimate, (uint32_t)OOK_MAX_HIGH_LEVEL);
|
||||
}
|
||||
|
||||
} else if (sig_state == STATE_PULSE) {
|
||||
++numg;
|
||||
if (numg > 100) numg = 100;
|
||||
if (mag < (threshold - hysteresis)) {
|
||||
// check if really a bad value
|
||||
if (numg < 3) {
|
||||
// susp
|
||||
sig_state = STATE_GAP;
|
||||
} else {
|
||||
numg = 0;
|
||||
sig_state = STATE_GAP_START;
|
||||
}
|
||||
meashl = false; // low
|
||||
} else {
|
||||
high_estimate += mag / OOK_EST_HIGH_RATIO - high_estimate / OOK_EST_HIGH_RATIO;
|
||||
high_estimate = std::max(high_estimate, min_high_level);
|
||||
high_estimate = std::min(high_estimate, (uint32_t)OOK_MAX_HIGH_LEVEL);
|
||||
meashl = true; // still high
|
||||
}
|
||||
} else if (sig_state == STATE_GAP_START) {
|
||||
++numg;
|
||||
if (mag > (threshold + hysteresis)) { // New pulse?
|
||||
sig_state = STATE_PULSE;
|
||||
meashl = true;
|
||||
} else if (numg >= 3) {
|
||||
sig_state = STATE_GAP;
|
||||
meashl = false; // gap
|
||||
}
|
||||
} else if (sig_state == STATE_GAP) {
|
||||
++numg;
|
||||
if (mag > (threshold + hysteresis)) { // New pulse?
|
||||
numg = 0;
|
||||
sig_state = STATE_PULSE;
|
||||
meashl = true;
|
||||
} else {
|
||||
meashl = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (meashl == currentHiLow && currentDuration < 30'000'000) // allow pass 'end' signal
|
||||
{
|
||||
currentDuration += nsPerDecSamp;
|
||||
} else { // called on change, so send the last duration and dir.
|
||||
if (currentDuration >= 30'000'000) sig_state = STATE_IDLE;
|
||||
if (protoList) protoList->feed(currentHiLow, currentDuration / 1000);
|
||||
currentDuration = nsPerDecSamp;
|
||||
currentHiLow = meashl;
|
||||
}
|
||||
}
|
||||
if (modulation == 1) {
|
||||
int32_t discrim = ((int32_t)im * fm_state.last_re) - ((int32_t)re * fm_state.last_im);
|
||||
fm_state.last_re = re;
|
||||
fm_state.last_im = im;
|
||||
fm_state.smoothed_discrim += (discrim - fm_state.smoothed_discrim) >> 4;
|
||||
|
||||
// --- FM Part (Simple 2-FSK) ---
|
||||
if (mag > (threshold / 2)) {
|
||||
const int32_t fm_hysteresis = 2000;
|
||||
bool new_level = fm_state.current_logic_level;
|
||||
if (fm_state.smoothed_discrim > fm_hysteresis) {
|
||||
new_level = true;
|
||||
} else if (fm_state.smoothed_discrim < -fm_hysteresis) {
|
||||
new_level = false;
|
||||
}
|
||||
if (new_level == fm_state.current_logic_level) {
|
||||
fm_state.buffer_count++;
|
||||
} else {
|
||||
int32_t duration_us = (fm_state.buffer_count * nsPerDecSamp) / 1000;
|
||||
if (duration_us > 15) {
|
||||
if (protoList) protoList->feed(fm_state.current_logic_level, duration_us);
|
||||
}
|
||||
fm_state.current_logic_level = new_level;
|
||||
fm_state.buffer_count = 1;
|
||||
}
|
||||
meashl = false; // low
|
||||
} else {
|
||||
high_estimate += mag / OOK_EST_HIGH_RATIO - high_estimate / OOK_EST_HIGH_RATIO;
|
||||
high_estimate = std::max(high_estimate, min_high_level);
|
||||
high_estimate = std::min(high_estimate, (uint32_t)OOK_MAX_HIGH_LEVEL);
|
||||
meashl = true; // still high
|
||||
}
|
||||
} else if (sig_state == STATE_GAP_START) {
|
||||
++numg;
|
||||
if (mag > (threshold + hysteresis)) { // New pulse?
|
||||
sig_state = STATE_PULSE;
|
||||
meashl = true;
|
||||
} else if (numg >= 3) {
|
||||
sig_state = STATE_GAP;
|
||||
meashl = false; // gap
|
||||
}
|
||||
} else if (sig_state == STATE_GAP) {
|
||||
++numg;
|
||||
if (mag > (threshold + hysteresis)) { // New pulse?
|
||||
numg = 0;
|
||||
sig_state = STATE_PULSE;
|
||||
meashl = true;
|
||||
} else {
|
||||
meashl = false;
|
||||
fm_state.buffer_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (meashl == currentHiLow && currentDuration < 30'000'000) // allow pass 'end' signal
|
||||
{
|
||||
currentDuration += nsPerDecSamp;
|
||||
} else { // called on change, so send the last duration and dir.
|
||||
if (currentDuration >= 30'000'000) sig_state = STATE_IDLE;
|
||||
if (protoList) protoList->feed(currentHiLow, currentDuration / 1000);
|
||||
currentDuration = nsPerDecSamp;
|
||||
currentHiLow = meashl;
|
||||
}
|
||||
|
||||
// fm part: -- NOT WORKING!!!! TODO FIX. AI code ;)
|
||||
int current_quad = get_quadrant(re, im);
|
||||
// Calculate Step (Current - Previous)
|
||||
int diff = current_quad - fm_state.prev_quad;
|
||||
// Handle Wrap-Around (crossing from Q3 to Q0 or Q0 to Q3)
|
||||
// 3 -> 0 should be +1 (CCW)
|
||||
// 0 -> 3 should be -1 (CW)
|
||||
if (diff == -3)
|
||||
diff = 1;
|
||||
else if (diff == 3)
|
||||
diff = -1;
|
||||
// Update History
|
||||
fm_state.prev_quad = current_quad;
|
||||
// Accumulate Rotation
|
||||
buffer_rotation_sum += diff;
|
||||
}
|
||||
|
||||
// fm finish:
|
||||
// 3. AUTO-CENTERING (DC BLOCKER)
|
||||
// Even with quadrant counting, "drift" (hand effect) makes the wheel spin
|
||||
// faster or slower. We need to subtract the average speed.
|
||||
// Update our "Average Speed" estimate
|
||||
// Note: buffer_rotation_sum is roughly proportional to frequency.
|
||||
fm_state.dc_offset = (fm_state.dc_offset * ((1 << DC_ALPHA) - 1) + buffer_rotation_sum) >> DC_ALPHA;
|
||||
// Remove the drift
|
||||
int32_t centered_rotation = buffer_rotation_sum - fm_state.dc_offset;
|
||||
// 4. LOW PASS FILTER
|
||||
const int32_t LPF_ALPHA = 4;
|
||||
fm_state.smoothed_error = (fm_state.smoothed_error * (LPF_ALPHA - 1) + centered_rotation) / LPF_ALPHA;
|
||||
// 5. DECISION LOGIC
|
||||
// Threshold is small now because we are counting quadrant steps.
|
||||
// Max steps per buffer (256 samples) is 256.
|
||||
// Typical FSK deviation might give you +/- 10 to 50 steps per buffer.
|
||||
const int32_t THRESHOLD = 3;
|
||||
bool new_level = fm_state.current_logic_level;
|
||||
if (fm_state.smoothed_error > THRESHOLD) {
|
||||
new_level = true;
|
||||
} else if (fm_state.smoothed_error < -THRESHOLD) {
|
||||
new_level = false;
|
||||
}
|
||||
// 6. TIMING OUTPUT
|
||||
if (new_level == fm_state.current_logic_level) {
|
||||
fm_state.buffer_count++;
|
||||
} else {
|
||||
// Output pulse duration
|
||||
int32_t duration_us = fm_state.buffer_count * 512;
|
||||
|
||||
if (duration_us > 250) {
|
||||
if (protoListFm) protoListFm->feed(fm_state.current_logic_level, duration_us);
|
||||
}
|
||||
fm_state.current_logic_level = new_level;
|
||||
fm_state.buffer_count = 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +154,14 @@ void SubCarProcessor::configure(const SubGhzFPRxConfigureMessage& message) {
|
||||
// constexpr size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor; //unused
|
||||
// constexpr size_t decim_1_output_fs = decim_0_output_fs / decim_1.decimation_factor; //unused
|
||||
|
||||
if (modulation != message.modulation) {
|
||||
// reload protos to reset them all
|
||||
if (protoList) {
|
||||
delete protoList;
|
||||
}
|
||||
protoList = new SubCarProtos();
|
||||
}
|
||||
modulation = message.modulation;
|
||||
baseband_fs = message.sampling_rate;
|
||||
baseband_thread.set_sampling_rate(baseband_fs);
|
||||
nsPerDecSamp = 1'000'000'000 / baseband_fs * 8; // Scaled it due to less array buffer sampes due to /8 decimation. 250 nseg (4Mhz) * 8
|
||||
|
||||
@@ -56,8 +56,8 @@ class SubCarProcessor : public BasebandProcessor {
|
||||
uint32_t low_estimate = 100;
|
||||
uint32_t high_estimate = 12000;
|
||||
uint32_t min_high_level = 10;
|
||||
uint8_t numg = 0; // count of matched signals to filter spikes
|
||||
size_t baseband_fs = 0; // will be set later by configure message
|
||||
uint8_t numg = 0; // count of matched signals to filter spikes
|
||||
size_t baseband_fs = 4'000'000; // will be set later by configure message
|
||||
uint32_t nsPerDecSamp = 0;
|
||||
|
||||
/* Array Buffer aux. used in decim0 and decim1 IQ c16 signed data ; (decim0 defines the max length of the array) */
|
||||
@@ -74,20 +74,20 @@ class SubCarProcessor : public BasebandProcessor {
|
||||
uint32_t threshold = 0x0630;
|
||||
bool currentHiLow = false;
|
||||
bool configured{false};
|
||||
uint8_t mode = 0; // 0 = am, 1 = fm
|
||||
|
||||
// fm part:
|
||||
struct DemodFMState {
|
||||
int prev_quad = 0; // Stores 0, 1, 2, or 3
|
||||
int32_t dc_offset = 0;
|
||||
int32_t smoothed_error = 0;
|
||||
bool current_logic_level = false;
|
||||
uint32_t buffer_count = 0;
|
||||
int16_t last_re = 0; // Store previous Real sample
|
||||
int16_t last_im = 0;
|
||||
int32_t smoothed_discrim = 0;
|
||||
};
|
||||
DemodFMState fm_state{};
|
||||
|
||||
FProtoListGeneral* protoList = new SubCarProtos(); // holds all the protocols we can parse
|
||||
FProtoListGeneral* protoListFm = new SubCarProtos(); // holds all the protocols we can parse, but for fm (dupe, bc most of it is dual)
|
||||
uint8_t modulation = 0; // 0 am, 1 fm
|
||||
|
||||
FProtoListGeneral* protoList = new SubCarProtos(); // holds all the protocols we can parse
|
||||
void configure(const SubGhzFPRxConfigureMessage& message);
|
||||
|
||||
/* NB: Threads should be the last members in the class definition. */
|
||||
|
||||
@@ -1681,4 +1681,362 @@ static constexpr fir_taps_real<24> taps_BTLE_Dual_PHY = {
|
||||
3}},
|
||||
};
|
||||
|
||||
static constexpr fir_taps_real<63> taps_2k0_am_lpf_channel = {
|
||||
.low_frequency_normalized = -2000.0f / 48000.0f,
|
||||
.high_frequency_normalized = 2000.0f / 48000.0f,
|
||||
.transition_normalized = 1500.0f / 48000.0f,
|
||||
.taps = {{
|
||||
26,
|
||||
29,
|
||||
31,
|
||||
33,
|
||||
32,
|
||||
28,
|
||||
18,
|
||||
0,
|
||||
-26,
|
||||
-61,
|
||||
-104,
|
||||
-152,
|
||||
-202,
|
||||
-246,
|
||||
-279,
|
||||
-292,
|
||||
-277,
|
||||
-227,
|
||||
-136,
|
||||
0,
|
||||
182,
|
||||
409,
|
||||
673,
|
||||
968,
|
||||
1280,
|
||||
1595,
|
||||
1899,
|
||||
2174,
|
||||
2407,
|
||||
2583,
|
||||
2693,
|
||||
2731,
|
||||
2693,
|
||||
2583,
|
||||
2407,
|
||||
2174,
|
||||
1899,
|
||||
1595,
|
||||
1280,
|
||||
968,
|
||||
673,
|
||||
409,
|
||||
182,
|
||||
0,
|
||||
-136,
|
||||
-227,
|
||||
-277,
|
||||
-292,
|
||||
-279,
|
||||
-246,
|
||||
-202,
|
||||
-152,
|
||||
-104,
|
||||
-61,
|
||||
-26,
|
||||
0,
|
||||
18,
|
||||
28,
|
||||
32,
|
||||
33,
|
||||
31,
|
||||
29,
|
||||
26,
|
||||
}},
|
||||
};
|
||||
|
||||
static constexpr fir_taps_real<64> taps_1K5_FM_channel = {
|
||||
.low_frequency_normalized = 300.0f / 48000.0f,
|
||||
.high_frequency_normalized = 1800.0f / 48000.0f,
|
||||
.transition_normalized = 1000.0f / 48000.0f,
|
||||
.taps = {{
|
||||
24,
|
||||
22,
|
||||
20,
|
||||
15,
|
||||
9,
|
||||
-2,
|
||||
-18,
|
||||
-39,
|
||||
-66,
|
||||
-98,
|
||||
-132,
|
||||
-167,
|
||||
-198,
|
||||
-220,
|
||||
-228,
|
||||
-217,
|
||||
-181,
|
||||
-117,
|
||||
-19,
|
||||
112,
|
||||
277,
|
||||
474,
|
||||
697,
|
||||
941,
|
||||
1197,
|
||||
1454,
|
||||
1702,
|
||||
1930,
|
||||
2128,
|
||||
2285,
|
||||
2394,
|
||||
2451,
|
||||
2451,
|
||||
2394,
|
||||
2285,
|
||||
2128,
|
||||
1930,
|
||||
1702,
|
||||
1454,
|
||||
1197,
|
||||
941,
|
||||
697,
|
||||
474,
|
||||
277,
|
||||
112,
|
||||
-19,
|
||||
-117,
|
||||
-181,
|
||||
-217,
|
||||
-228,
|
||||
-220,
|
||||
-198,
|
||||
-167,
|
||||
-132,
|
||||
-98,
|
||||
-66,
|
||||
-39,
|
||||
-18,
|
||||
-2,
|
||||
9,
|
||||
15,
|
||||
20,
|
||||
22,
|
||||
24,
|
||||
}},
|
||||
};
|
||||
|
||||
static constexpr fir_taps_real<63> taps_1k5_dsb_lpf = {
|
||||
.low_frequency_normalized = -1500.0f / 48000.0f,
|
||||
.high_frequency_normalized = 1500.0f / 48000.0f,
|
||||
.transition_normalized = 1000.0f / 48000.0f,
|
||||
.taps = {{
|
||||
0,
|
||||
0,
|
||||
-1,
|
||||
-5,
|
||||
-10,
|
||||
-19,
|
||||
-31,
|
||||
-46,
|
||||
-64,
|
||||
-82,
|
||||
-99,
|
||||
-111,
|
||||
-113,
|
||||
-100,
|
||||
-64,
|
||||
0,
|
||||
99,
|
||||
239,
|
||||
424,
|
||||
655,
|
||||
932,
|
||||
1251,
|
||||
1605,
|
||||
1983,
|
||||
2372,
|
||||
2757,
|
||||
3120,
|
||||
3447,
|
||||
3719,
|
||||
3925,
|
||||
4053,
|
||||
4096,
|
||||
4053,
|
||||
3925,
|
||||
3719,
|
||||
3447,
|
||||
3120,
|
||||
2757,
|
||||
2372,
|
||||
1983,
|
||||
1605,
|
||||
1251,
|
||||
932,
|
||||
655,
|
||||
424,
|
||||
239,
|
||||
99,
|
||||
0,
|
||||
-64,
|
||||
-100,
|
||||
-113,
|
||||
-111,
|
||||
-99,
|
||||
-82,
|
||||
-64,
|
||||
-46,
|
||||
-31,
|
||||
-19,
|
||||
-10,
|
||||
-5,
|
||||
-1,
|
||||
0,
|
||||
0,
|
||||
}},
|
||||
};
|
||||
|
||||
// GAIN: 4.0x
|
||||
static constexpr fir_taps_complex<63> taps_1k5_USB_channel = {
|
||||
.low_frequency_normalized = 300.0f / 48000.0f,
|
||||
.high_frequency_normalized = 1800.0f / 48000.0f,
|
||||
.transition_normalized = 300.0f / 48000.0f,
|
||||
.taps = {{
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{2, 0},
|
||||
{4, 2},
|
||||
{10, 6},
|
||||
{17, 14},
|
||||
{27, 29},
|
||||
{37, 53},
|
||||
{45, 89},
|
||||
{49, 140},
|
||||
{41, 207},
|
||||
{17, 290},
|
||||
{-31, 389},
|
||||
{-109, 499},
|
||||
{-227, 614},
|
||||
{-387, 725},
|
||||
{-595, 819},
|
||||
{-850, 884},
|
||||
{-1147, 904},
|
||||
{-1477, 866},
|
||||
{-1827, 757},
|
||||
{-2179, 569},
|
||||
{-2512, 297},
|
||||
{-2804, -55},
|
||||
{-3031, -480},
|
||||
{-3173, -962},
|
||||
{-3213, -1481},
|
||||
{-3142, -2011},
|
||||
{-2955, -2524},
|
||||
{-2658, -2991},
|
||||
{-2262, -3386},
|
||||
{-1788, -3685},
|
||||
{-1258, -3873},
|
||||
{-703, -3939},
|
||||
{-153, -3884},
|
||||
{366, -3713},
|
||||
{826, -3440},
|
||||
{1208, -3087},
|
||||
{1499, -2677},
|
||||
{1693, -2236},
|
||||
{1789, -1789},
|
||||
{1796, -1359},
|
||||
{1726, -966},
|
||||
{1594, -624},
|
||||
{1420, -341},
|
||||
{1220, -120},
|
||||
{1012, 40},
|
||||
{809, 144},
|
||||
{622, 202},
|
||||
{460, 223},
|
||||
{324, 217},
|
||||
{217, 193},
|
||||
{137, 160},
|
||||
{80, 125},
|
||||
{42, 91},
|
||||
{19, 62},
|
||||
{6, 39},
|
||||
{0, 22},
|
||||
{-1, 11},
|
||||
{-1, 5},
|
||||
{-1, 1},
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
}},
|
||||
};
|
||||
|
||||
// GAIN: 4.0x
|
||||
static constexpr fir_taps_complex<63> taps_1k5_LSB_channel = {
|
||||
.low_frequency_normalized = 300.0f / 48000.0f,
|
||||
.high_frequency_normalized = 1800.0f / 48000.0f,
|
||||
.transition_normalized = 300.0f / 48000.0f,
|
||||
.taps = {{
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
{2, 0},
|
||||
{4, -2},
|
||||
{10, -6},
|
||||
{17, -14},
|
||||
{27, -29},
|
||||
{37, -53},
|
||||
{45, -89},
|
||||
{49, -140},
|
||||
{41, -207},
|
||||
{17, -290},
|
||||
{-31, -389},
|
||||
{-109, -499},
|
||||
{-227, -614},
|
||||
{-387, -725},
|
||||
{-595, -819},
|
||||
{-850, -884},
|
||||
{-1147, -904},
|
||||
{-1477, -866},
|
||||
{-1827, -757},
|
||||
{-2179, -569},
|
||||
{-2512, -297},
|
||||
{-2804, 55},
|
||||
{-3031, 480},
|
||||
{-3173, 962},
|
||||
{-3213, 1481},
|
||||
{-3142, 2011},
|
||||
{-2955, 2524},
|
||||
{-2658, 2991},
|
||||
{-2262, 3386},
|
||||
{-1788, 3685},
|
||||
{-1258, 3873},
|
||||
{-703, 3939},
|
||||
{-153, 3884},
|
||||
{366, 3713},
|
||||
{826, 3440},
|
||||
{1208, 3087},
|
||||
{1499, 2677},
|
||||
{1693, 2236},
|
||||
{1789, 1789},
|
||||
{1796, 1359},
|
||||
{1726, 966},
|
||||
{1594, 624},
|
||||
{1420, 341},
|
||||
{1220, 120},
|
||||
{1012, -40},
|
||||
{809, -144},
|
||||
{622, -202},
|
||||
{460, -223},
|
||||
{324, -217},
|
||||
{217, -193},
|
||||
{137, -160},
|
||||
{80, -125},
|
||||
{42, -91},
|
||||
{19, -62},
|
||||
{6, -39},
|
||||
{0, -22},
|
||||
{-1, -11},
|
||||
{-1, -5},
|
||||
{-1, -1},
|
||||
{0, 0},
|
||||
{0, 0},
|
||||
}},
|
||||
};
|
||||
|
||||
#endif /*__DSP_FIR_TAPS_H__*/
|
||||
|
||||
@@ -152,6 +152,7 @@ class Message {
|
||||
TXDisabled = 94,
|
||||
MorseTXConfigure = 95,
|
||||
MorseTXkey = 96,
|
||||
StreamTXConfiguration = 97,
|
||||
MAX
|
||||
};
|
||||
|
||||
@@ -1711,6 +1712,7 @@ class MorseRXDataMessage : public Message {
|
||||
: Message{ID::MorseRXData} {}
|
||||
int32_t state_durations[5] = {0}; // positive: high, negative: low
|
||||
uint8_t state_cnt = 0;
|
||||
bool clipped = false;
|
||||
const uint8_t maxptr = 4;
|
||||
};
|
||||
|
||||
@@ -1757,4 +1759,15 @@ class MorseTXkeyMessage : public Message {
|
||||
bool key_down = false;
|
||||
};
|
||||
|
||||
class StreamTXConfigurationMessage : public Message {
|
||||
public:
|
||||
constexpr StreamTXConfigurationMessage(uint32_t deviation, uint8_t mode)
|
||||
: Message{ID::StreamTXConfiguration},
|
||||
deviation{deviation},
|
||||
mode{mode} {}
|
||||
|
||||
uint32_t deviation = 60000; // used in 2fsk
|
||||
uint8_t mode = 0; // am = 0, 2fsk = 1
|
||||
};
|
||||
|
||||
#endif /*__MESSAGE_H__*/
|
||||
|
||||
@@ -650,7 +650,13 @@ void GeoMap::move(const float lon, const float lat) {
|
||||
} else {
|
||||
if (is_changed) {
|
||||
set_osm_max_zoom();
|
||||
redraw_map = true;
|
||||
double global_center_px = lon_to_pixel_x_tile(lon_, map_osm_zoom);
|
||||
double global_center_py = lat_to_pixel_y_tile(lat_, map_osm_zoom);
|
||||
// Redraw if viewport moved by at least 1 pixel (includes zoom level changes)
|
||||
if (abs(global_center_px - (r.width() / 2.0) - viewport_top_left_px) >= 1.0 ||
|
||||
abs(global_center_py - (r.height() / 2.0) - viewport_top_left_py) >= 1.0) {
|
||||
redraw_map = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
Submodule hackrf updated: 22e44face8...40f441900a
Reference in New Issue
Block a user