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 #1501

Closed
wants to merge 4 commits into from
Closed
Changes from 1 commit
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
178 changes: 154 additions & 24 deletions applications/subghz/views/subghz_frequency_analyzer.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,23 @@

#include <assets_icons.h>

#define TAG "frequency_analyzer"

#define TRIGGER_MIN -85.5
#define TRIGGER_MAX -28.5

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 +37,20 @@ struct SubGhzFrequencyAnalyzer {
SubGhzFrequencyAnalyzerCallback callback;
void* context;
bool locked;
float rssi_last;
uint32_t frequency_last;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this does not solve the underlying issue.

If one is scanning a busy area, the "last" would flicker just as much as the "current", making it unreadable the same way as the existing implementation.

#opinionated

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this does not solve the underlying issue.

If one is scanning a busy area, the "last" would flicker just as much as the "current", making it unreadable the same way as the existing implementation.

#opinionated

No, it's updated with the last value after there is no signal for a while.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. I see. Well, the last time I used c was about 15 years ago 😅
Anyhow, it would be great to reuse the subghz_history and utilize the up and down arrows to navigate in the history. What do you think?
I have been trying to do just that, but unfortunately c was really really a long time ago.. 😞

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyhow, it would be great to reuse the subghz_history and utilize the up and down arrows to navigate in the history. What do you think?

I think that it's good idea but i'm not ready to implement it now. Maybe later.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello,

What's the best way to contribute to the "history feature" ?
Waiting for this PR to be validated ? Forking your fork & PR to it ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello,

What's the best way to contribute to the "history feature" ? Waiting for this PR to be validated ? Forking your fork & PR to it ?

Not sure. Let's wait for PR to be validated.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Damn I wish I was better at cpp! 😅

uint32_t frequency_last_vis;
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 +63,168 @@ 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;
if(rssi) {
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;
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 && rssi_last) {
int max_x = (int)((rssi_last + 87.0) / 3) * 2;
//if (!(max_x % 8)) max_x -= 2;
int max_h = (int)((rssi_last + 87.0) / 3) + 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 > TRIGGER_MIN) {
trigger = (trigger + 90) / 3;
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);
}

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

if(event->key == InputKeyBack) {
bool need_redraw = 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 -= 1.5;
if (instance->trigger < TRIGGER_MIN) instance->trigger = TRIGGER_MIN;
break;
default:
case InputKeyRight:
instance->trigger += 1.5;
if (instance->trigger > TRIGGER_MAX) instance->trigger = TRIGGER_MAX;
break;
}
if (instance->trigger > TRIGGER_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_vis;
model->trigger = instance->trigger;
return true;
});
}

return true;
}

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);
notification_message(instance->notifications, &sequence_hw_blink);
instance->frequency_last_vis = instance->frequency_last;
}
instance->triggered = false;
} else if((rssi != 0.f) && (!instance->locked)) {
if(instance->callback) {
instance->callback(SubGhzCustomEventSceneAnalyzerLock, instance->context);
}
}

if ((rssi != 0.f) && (frequency != 0)) {
// Threre is some signal
FURI_LOG_I(TAG, "rssi = %.2f, frequency = %d", (double)rssi, frequency);
if (!instance->triggered && ((instance->trigger <= TRIGGER_MIN) || (rssi >= instance->trigger))) {
// 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");
}
if (instance->triggered) {
// Update values
if (rssi > instance->rssi_last) instance->rssi_last = rssi;
instance->frequency_last = frequency;
}
}

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_vis;
model->trigger = instance->trigger;
return true;
});
}
Expand All @@ -110,6 +233,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 +247,19 @@ void subghz_frequency_analyzer_enter(void* context) {

subghz_frequency_analyzer_worker_start(instance->worker);

instance->rssi_last = 0;
instance->frequency_last = 0;
instance->frequency_last_vis = 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 +268,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 +294,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