| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- /* 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: ");
- /* We will know the size of the preset data once we reach the end
- * of the registers (null address). For now it's INT_MAX. */
- int preset_data_size = INT_MAX;
- bool patable_reached = false;
- for(int j = 0; j <= preset_data_size; j += 2) {
- // End reached, set the size to write the remaining 8 bytes (PATABLE)
- if(!patable_reached && regs[j] == 0) {
- preset_data_size = j + 8;
- patable_reached = true;
- }
- furi_string_cat_printf(custom, "%02X %02X ", (int)regs[j], (int)regs[j + 1]);
- }
- size_t len = furi_string_size(custom);
- 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;
- }
|