Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New frequency analyzer #43

Merged
merged 1 commit into from
Aug 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

#define TAG "SubghzFrequencyAnalyzerWorker"

#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -95.0f
#define SUBGHZ_FREQUENCY_ANALYZER_THRESHOLD -97.0f

static const uint8_t subghz_preset_ook_58khz[][2] = {
{CC1101_MDMCFG4, 0b11110111}, // Rx BW filter is 58.035714kHz
Expand Down
12 changes: 2 additions & 10 deletions applications/subghz/scenes/subghz_scene_frequency_analyzer.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,8 @@ void subghz_scene_frequency_analyzer_on_enter(void* context) {
}

bool subghz_scene_frequency_analyzer_on_event(void* context, SceneManagerEvent event) {
SubGhz* subghz = context;
if(event.type == SceneManagerEventTypeCustom) {
if(event.event == SubGhzCustomEventSceneAnalyzerLock) {
notification_message(subghz->notifications, &sequence_set_green_255);
return true;
} else if(event.event == SubGhzCustomEventSceneAnalyzerUnlock) {
notification_message(subghz->notifications, &sequence_reset_rgb);
return true;
}
}
UNUSED(context);
UNUSED(event);
return false;
}

Expand Down
215 changes: 184 additions & 31 deletions applications/subghz/views/subghz_frequency_analyzer.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,31 @@
#include <furi_hal.h>
#include <input/input.h>
#include <notification/notification_messages.h>
#include <gui/elements.h>
#include "../helpers/subghz_frequency_analyzer_worker.h"

#include <assets_icons.h>

#define TAG "frequency_analyzer"

#define RSSI_MIN -101
#define RSSI_MAX -60
#define RSSI_SCALE 2
#define TRIGGER_STEP 1
#define TRIGGER_MIN RSSI_MIN + RSSI_SCALE * 2

static const NotificationSequence sequence_hw_blink = {
&message_blink_start_10,
&message_blink_set_color_cyan,
&message_do_not_reset,
NULL,
};

static const NotificationSequence sequence_hw_blink_stop = {
&message_blink_stop,
NULL,
};

typedef enum {
SubGhzFrequencyAnalyzerStatusIDLE,
} SubGhzFrequencyAnalyzerStatus;
Expand All @@ -20,11 +41,19 @@ struct SubGhzFrequencyAnalyzer {
SubGhzFrequencyAnalyzerCallback callback;
void* context;
bool locked;
float rssi_last;
uint32_t frequency_last;
float trigger;
bool triggered;
NotificationApp* notifications;
};

typedef struct {
uint32_t frequency;
uint32_t frequency_last;
float rssi;
float rssi_last;
float trigger;
} SubGhzFrequencyAnalyzerModel;

void subghz_frequency_analyzer_set_callback(
Expand All @@ -37,71 +66,189 @@ void subghz_frequency_analyzer_set_callback(
subghz_frequency_analyzer->context = context;
}

void subghz_frequency_analyzer_draw_rssi(Canvas* canvas, float rssi) {
uint8_t x = 48;
uint8_t y = 56;
uint8_t column_number = 0;
void subghz_frequency_analyzer_draw_rssi(
Canvas* canvas,
float rssi,
float rssi_last,
float trigger,
uint8_t x,
uint8_t y) {
// Current RSSI
if(rssi) {
rssi = (rssi + 90) / 3;
rssi = (rssi - RSSI_MIN) / RSSI_SCALE;
if(rssi > 20) rssi = 20;
uint8_t column_number = 0;
for(size_t i = 1; i < (uint8_t)rssi; i++) {
if(i > 20) break;
if(i % 4) {
column_number++;
canvas_draw_box(canvas, x + 2 * i, y - column_number, 2, 4 + column_number);
}
}
}

// Last RSSI
if(rssi_last) {
int max_x = (int)((rssi_last - RSSI_MIN) / RSSI_SCALE - 1) * 2;
//if(!(max_x % 8)) max_x -= 2;
int max_h = (int)((rssi_last - RSSI_MIN) / RSSI_SCALE - 1) + 4;
max_h -= (max_h / 4) + 3;
if(max_x > 38) max_h = 38;
if(max_h > 19) max_h = 19;
if(max_x >= 0 && max_h > 0) {
canvas_draw_line(canvas, x + max_x + 1, y - max_h, x + max_x + 1, y + 3);
}
}

// Trigger cursor
if(trigger >= RSSI_MIN + RSSI_SCALE * 2) {
trigger = (trigger - RSSI_MIN) / RSSI_SCALE;
uint8_t tr_x = x + 2 * trigger - 2;
canvas_draw_dot(canvas, tr_x, y + 4);
canvas_draw_line(canvas, tr_x - 1, y + 5, tr_x + 1, y + 5);
}

canvas_draw_line(canvas, x + 2, y + 3, x + 39, y + 3);
}

void subghz_frequency_analyzer_draw(Canvas* canvas, SubGhzFrequencyAnalyzerModel* model) {
char buffer[64];

// Title
canvas_set_color(canvas, ColorBlack);
canvas_set_font(canvas, FontSecondary);
canvas_draw_str(canvas, 20, 8, "Frequency Analyzer");

canvas_draw_str(canvas, 28, 60, "RSSI");
subghz_frequency_analyzer_draw_rssi(canvas, model->rssi);
// RSSI
canvas_draw_str(canvas, 33, 62, "RSSI");
subghz_frequency_analyzer_draw_rssi(
canvas, model->rssi, model->rssi_last, model->trigger, 55, 58);

//Frequency
// Frequency
canvas_set_font(canvas, FontBigNumbers);
snprintf(
buffer,
sizeof(buffer),
"%03ld.%03ld",
model->frequency / 1000000 % 1000,
model->frequency / 1000 % 1000);
canvas_draw_str(canvas, 8, 35, buffer);
canvas_draw_icon(canvas, 96, 24, &I_MHz_25x11);
canvas_draw_str(canvas, 8, 30, buffer);
canvas_draw_icon(canvas, 96, 19, &I_MHz_25x11);

// Last detected frequency
canvas_set_font(canvas, FontSecondary);
if(model->frequency_last) {
snprintf(
buffer,
sizeof(buffer),
"Last: %03ld.%03ld MHz",
model->frequency_last / 1000000 % 1000,
model->frequency_last / 1000 % 1000);
} else {
strcpy(buffer, "Last: ---.--- MHz");
}
canvas_draw_str(canvas, 9, 42, buffer);

// Buttons hint
elements_button_left(canvas, "T-");
elements_button_right(canvas, "T+");
}

bool subghz_frequency_analyzer_input(InputEvent* event, void* context) {
furi_assert(context);
SubGhzFrequencyAnalyzer* instance = context;

bool need_redraw = false;

if(event->key == InputKeyBack) {
return false;
if(event->key == InputKeyBack) return false;

if(((event->type == InputTypePress) || (event->type == InputTypeRepeat)) &&
((event->key == InputKeyLeft) || (event->key == InputKeyRight))) {
// Trigger setup
switch(event->key) {
case InputKeyLeft:
instance->trigger -= TRIGGER_STEP;
if(instance->trigger < RSSI_MIN + RSSI_SCALE * 2) instance->trigger = TRIGGER_MIN;
break;
default:
case InputKeyRight:
if(instance->trigger < RSSI_MIN + RSSI_SCALE * 2)
instance->trigger = TRIGGER_MIN;
else
instance->trigger += TRIGGER_STEP;
if(instance->trigger > RSSI_MAX) instance->trigger = RSSI_MAX;
break;
}
if(instance->trigger > RSSI_MIN)
FURI_LOG_I(TAG, "trigger = %.1f", (double)instance->trigger);
else
FURI_LOG_I(TAG, "trigger disabled");
need_redraw = true;
}

if(need_redraw) {
SubGhzFrequencyAnalyzer* instance = context;
with_view_model(
instance->view, (SubGhzFrequencyAnalyzerModel * model) {
model->rssi_last = instance->rssi_last;
model->frequency_last = instance->frequency_last;
model->trigger = instance->trigger;
return true;
});
}

return true;
}

uint32_t round_int(uint32_t value, uint8_t n) {
// Round value
uint8_t on = n;
while(n--) {
uint8_t i = value % 10;
value /= 10;
if(i >= 5) value++;
}
while(on--) value *= 10;
return value;
}

void subghz_frequency_analyzer_pair_callback(void* context, uint32_t frequency, float rssi) {
furi_assert(context);
SubGhzFrequencyAnalyzer* instance = context;

if((rssi == 0.f) && (instance->locked)) {
if(instance->callback) {
instance->callback(SubGhzCustomEventSceneAnalyzerUnlock, instance->context);
}
} else if((rssi != 0.f) && (!instance->locked)) {
if(instance->callback) {
instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context);
notification_message(instance->notifications, &sequence_hw_blink);
instance->triggered = false;
}

if((rssi != 0.f) && (frequency != 0)) {
// Threre is some signal
FURI_LOG_I(TAG, "rssi = %.2f, frequency = %d Hz", (double)rssi, frequency);
frequency = round_int(frequency, 3); // Round 299999990Hz to 300000000Hz
if((instance->trigger <= RSSI_MIN + RSSI_SCALE * 2) || (rssi >= instance->trigger)) {
if(!instance->triggered) {
// Triggered!
instance->triggered = true;
instance->rssi_last = rssi;
notification_message(instance->notifications, &sequence_hw_blink_stop);
notification_message(instance->notifications, &sequence_success);
FURI_LOG_D(TAG, "triggered");
}
// Update values
if(rssi > instance->rssi_last) instance->rssi_last = rssi;
instance->frequency_last = frequency;
} else {
instance->triggered = false;
}
}

instance->locked = (rssi != 0.f);
with_view_model(
instance->view, (SubGhzFrequencyAnalyzerModel * model) {
model->rssi = rssi;
model->rssi_last = instance->rssi_last;
model->frequency = frequency;
model->frequency_last = instance->frequency_last;
model->trigger = instance->trigger;
return true;
});
}
Expand All @@ -110,6 +257,10 @@ void subghz_frequency_analyzer_enter(void* context) {
furi_assert(context);
SubGhzFrequencyAnalyzer* instance = context;

// Notifications
instance->notifications = furi_record_open(RECORD_NOTIFICATION);
notification_message(instance->notifications, &sequence_hw_blink);

//Start worker
instance->worker = subghz_frequency_analyzer_worker_alloc(instance->context);

Expand All @@ -120,10 +271,18 @@ void subghz_frequency_analyzer_enter(void* context) {

subghz_frequency_analyzer_worker_start(instance->worker);

instance->rssi_last = 0;
instance->frequency_last = 0;
instance->trigger = TRIGGER_MIN;
instance->triggered = false;

with_view_model(
instance->view, (SubGhzFrequencyAnalyzerModel * model) {
model->rssi = 0;
model->rssi_last = 0;
model->frequency = 0;
model->frequency_last = 0;
model->trigger = instance->trigger;
return true;
});
}
Expand All @@ -132,21 +291,21 @@ void subghz_frequency_analyzer_exit(void* context) {
furi_assert(context);
SubGhzFrequencyAnalyzer* instance = context;

//Stop worker
// Stop blinking
notification_message(instance->notifications, &sequence_hw_blink_stop);

// Stop worker
if(subghz_frequency_analyzer_worker_is_running(instance->worker)) {
subghz_frequency_analyzer_worker_stop(instance->worker);
}
subghz_frequency_analyzer_worker_free(instance->worker);

with_view_model(
instance->view, (SubGhzFrequencyAnalyzerModel * model) {
model->rssi = 0;
return true;
});
furi_record_close(RECORD_NOTIFICATION);
}

SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() {
SubGhzFrequencyAnalyzer* instance = malloc(sizeof(SubGhzFrequencyAnalyzer));
furi_assert(instance);

// View allocation and configuration
instance->view = view_alloc();
Expand All @@ -158,12 +317,6 @@ SubGhzFrequencyAnalyzer* subghz_frequency_analyzer_alloc() {
view_set_enter_callback(instance->view, subghz_frequency_analyzer_enter);
view_set_exit_callback(instance->view, subghz_frequency_analyzer_exit);

with_view_model(
instance->view, (SubGhzFrequencyAnalyzerModel * model) {
model->rssi = 0;
return true;
});

return instance;
}

Expand Down