-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathsignal_file.c
141 lines (124 loc) · 5.52 KB
/
signal_file.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/* Copyright (C) 2023 Salvatore Sanfilippo -- All Rights Reserved
* Copyright (C) 2023 Maciej Wojtasik -- All Rights Reserved
* See the LICENSE file for information about the license. */
#include "app.h"
#include <stream/stream.h>
#include <flipper_format/flipper_format_i.h>
/* ========================= Signal file operations ========================= */
/* This function saves the current logical signal on disk. What is saved here
* is not the signal as level and duration as we received it from CC1101,
* but it's logical representation stored in the app->msg_info bitmap, where
* each 1 or 0 means a puls or gap for the specified short pulse duration time
* (te). */
bool save_signal(ProtoViewApp *app, const char *filename) {
/* We have a message at all? */
if (app->msg_info == NULL || app->msg_info->pulses_count == 0) return false;
Storage *storage = furi_record_open(RECORD_STORAGE);
FlipperFormat *file = flipper_format_file_alloc(storage);
Stream *stream = flipper_format_get_raw_stream(file);
FuriString *file_content = NULL;
bool success = true;
if (flipper_format_file_open_always(file, filename)) {
/* Write the file header. */
FuriString *file_content = furi_string_alloc();
const char *preset_id = ProtoViewModulations[app->modulation].id;
furi_string_printf(file_content,
"Filetype: Flipper SubGhz RAW File\n"
"Version: 1\n"
"Frequency: %ld\n"
"Preset: %s\n",
app->frequency,
preset_id ? preset_id : "FuriHalSubGhzPresetCustom");
/* For custom modulations, we need to emit a set of registers. */
if (preset_id == NULL) {
FuriString *custom = furi_string_alloc();
uint8_t *regs = ProtoViewModulations[app->modulation].custom;
furi_string_printf(custom,
"Custom_preset_module: CC1101\n"
"Custom_preset_data: ");
for (int j = 0; regs[j]; j += 2) {
furi_string_cat_printf(custom, "%02X %02X ",
(int)regs[j], (int)regs[j+1]);
}
size_t len = furi_string_size(file_content);
furi_string_set_char(custom,len-1,'\n');
furi_string_cat(file_content,custom);
furi_string_free(custom);
}
/* We always save raw files. */
furi_string_cat_printf(file_content,
"Protocol: RAW\n"
"RAW_Data: -10000\n"); // Start with 10 ms of gap
/* Write header. */
size_t len = furi_string_size(file_content);
if (stream_write(stream,
(uint8_t*) furi_string_get_cstr(file_content), len)
!= len)
{
FURI_LOG_W(TAG, "Short write to file");
success = false;
goto write_err;
}
furi_string_reset(file_content);
/* Write raw data sections. The Flipper subghz parser can't handle
* too much data on a single line, so we generate a new one
* every few samples. */
uint32_t this_line_samples = 0;
uint32_t max_line_samples = 100;
uint32_t idx = 0; // Iindex in the signal bitmap.
ProtoViewMsgInfo *i = app->msg_info;
while(idx < i->pulses_count) {
bool level = bitmap_get(i->bits,i->bits_bytes,idx);
uint32_t te_times = 1;
idx++;
/* Count the duration of the current pulse/gap. */
while(idx < i->pulses_count &&
bitmap_get(i->bits,i->bits_bytes,idx) == level)
{
te_times++;
idx++;
}
// Invariant: after the loop 'idx' is at the start of the
// next gap or pulse.
int32_t dur = (int32_t)i->short_pulse_dur * te_times;
if (level == 0) dur = -dur; /* Negative is gap in raw files. */
/* Emit the sample. If this is the first sample of the line,
* also emit the RAW_Data: field. */
if (this_line_samples == 0)
furi_string_cat_printf(file_content,"RAW_Data: ");
furi_string_cat_printf(file_content,"%d ",(int)dur);
this_line_samples++;
/* Store the current set of samples on disk, when we reach a
* given number or the end of the signal. */
bool end_reached = (idx == i->pulses_count);
if (this_line_samples == max_line_samples || end_reached) {
/* If that's the end, terminate the signal with a long
* gap. */
if (end_reached) furi_string_cat_printf(file_content,"-10000 ");
/* We always have a trailing space in the last sample. Make it
* a newline. */
size_t len = furi_string_size(file_content);
furi_string_set_char(file_content,len-1,'\n');
if (stream_write(stream,
(uint8_t*) furi_string_get_cstr(file_content),
len) != len)
{
FURI_LOG_W(TAG, "Short write to file");
success = false;
goto write_err;
}
/* Prepare for next line. */
furi_string_reset(file_content);
this_line_samples = 0;
}
}
} else {
success = false;
FURI_LOG_W(TAG, "Unable to open file");
}
write_err:
furi_record_close(RECORD_STORAGE);
flipper_format_free(file);
if (file_content != NULL) furi_string_free(file_content);
return success;
}