#include "live.h" #include "common/dsp_source_sink/dsp_sample_source.h" #include "common/dsp/resamp/smart_resampler.h" #include #include "logger.h" #include "common/cli_utils.h" #include "common/dsp/io/file_sink.h" #include "init.h" #include "common/dsp/path/splitter.h" #include "common/dsp/fft/fft_pan.h" #include "webserver.h" // Catch CTRL+C to exit live properly! bool rec_should_exit = false; void sig_handler_rec(int signo) { if (signo == SIGINT || signo == SIGTERM) rec_should_exit = true; } int main_record(int argc, char *argv[]) { if (argc < 5) // Check overall command { logger->error("Usage : " + std::string(argv[0]) + " record [output_baseband (without extension!)] [additional options as required]"); logger->error("Extra options (examples. Any parameter used in sources can be used here) :"); logger->error(" --samplerate [baseband_samplerate] --baseband_format [f32/s16/s8/u8/w16/ziq] --dc_block --iq_swap"); logger->error(" --source [airspy/rtlsdr/etc] --gain 20 --bias"); logger->error("As well as --timeout in seconds"); logger->error("Sample command :"); logger->error("./satdump record baseband_name --source airspy --samplerate 6e6 --frequency 1701.3e6 --general_gain 18 --bias --timeout 780"); return 1; } // Init SatDump satdump::initSatdump(); completeLoggerInit(); std::string output_file = argv[2]; // Parse flags nlohmann::json parameters = parse_common_flags(argc - 3, &argv[3]); uint64_t samplerate; uint64_t frequency; uint64_t timeout; std::string handler_id; uint64_t hdl_dev_id = 0; double decimation = 1; try { samplerate = parameters["samplerate"].get(); frequency = parameters["frequency"].get(); timeout = parameters.contains("timeout") ? parameters["timeout"].get() : 0; handler_id = parameters["source"].get(); if (parameters.contains("decimation")) decimation = parameters["decimation"].get(); if (parameters.contains("source_id")) hdl_dev_id = parameters["source_id"].get(); } catch (std::exception &e) { logger->error("Error parsing arguments! %s", e.what()); return 1; } // Create output dir if (std::filesystem::path(output_file).has_parent_path()) if (!std::filesystem::exists(std::filesystem::path(output_file).parent_path().string())) std::filesystem::create_directories(std::filesystem::path(output_file).parent_path().string()); // Get all sources dsp::registerAllSources(); std::vector source_tr = dsp::getAllAvailableSources(); dsp::SourceDescriptor selected_src; // Try to find it and check it's usable bool src_found = false; for (dsp::SourceDescriptor src : source_tr) { logger->debug("Device " + src.name); if (handler_id == src.source_type) { if (parameters.contains("source_id")) { #ifdef _WIN32 // Windows being cursed. TODO investigate further? It's uint64_t everywhere come on! char cmp_buff1[100]; char cmp_buff2[100]; snprintf(cmp_buff1, sizeof(cmp_buff1), "%d", hdl_dev_id); std::string cmp1 = cmp_buff1; snprintf(cmp_buff2, sizeof(cmp_buff2), "%d", src.unique_id); std::string cmp2 = cmp_buff2; if (cmp1 == cmp2) #else if (hdl_dev_id == src.unique_id) #endif { selected_src = src; src_found = true; } } else { selected_src = src; src_found = true; } } } if (!src_found) { logger->error("Could not find a handler for source type : %s!", handler_id.c_str()); return 1; } // Init source std::shared_ptr source_ptr = getSourceFromDescriptor(selected_src); source_ptr->open(); source_ptr->set_frequency(frequency); source_ptr->set_samplerate(samplerate); source_ptr->set_settings(parameters); std::unique_ptr> decim; std::unique_ptr splitter; std::unique_ptr fft; bool webserver_already_set = false; // Attempt to start the source try { source_ptr->start(); } catch (std::exception &e) { logger->error("Fatal error starting device : " + std::string(e.what())); return 1; } // Decimation if requested if (decimation > 1) { decim = std::make_unique>(source_ptr->output_stream, 1, decimation); decim->start(); logger->info("Setting up resampler..."); } // Optional FFT std::shared_ptr> final_stream = decimation > 1 ? decim->output_stream : source_ptr->output_stream; int fft_size = 0; if (parameters.contains("fft_enable")) { fft_size = parameters.contains("fft_size") ? parameters["fft_size"].get() : 512; int fft_rate = parameters.contains("fft_rate") ? parameters["fft_rate"].get() : 30; splitter = std::make_unique(source_ptr->output_stream); splitter->add_output("fft"); splitter->set_enabled("fft", true); final_stream = splitter->output_stream; fft = std::make_unique(splitter->get_output("fft")); fft->set_fft_settings(fft_size, samplerate / decimation, fft_rate); if (parameters.contains("fft_avgn")) fft->avg_num = parameters["fft_avgn"].get(); splitter->start(); fft->start(); } // Setup file sink std::shared_ptr file_sink = std::make_shared(final_stream); if (parameters.contains("fft_enable")) { webserver::handle_callback = [&file_sink, &fft, fft_size]() { nlohmann::json stats; stats["written"] = file_sink->get_written(); stats["written_raw"] = file_sink->get_written_raw(); for (int i = 0; i < fft_size; i++) stats["fft_values"][i] = fft->output_stream->writeBuf[i]; return stats.dump(4); }; webserver_already_set = true; } int ziq_bit_depth = 8; if (parameters.contains("ziq_depth")) ziq_bit_depth = parameters["ziq_depth"].get(); if (parameters["baseband_format"].get() == "ziq") logger->info("Using ZIQ Depth %d", ziq_bit_depth); if (parameters.contains("baseband_format")) { file_sink->set_output_sample_type(dsp::basebandTypeFromString(parameters["baseband_format"].get())); } else { logger->error("baseband_format flag is required!"); return 1; } file_sink->start(); file_sink->start_recording(output_file, samplerate / decimation, ziq_bit_depth); // If requested, boot up webserver if (parameters.contains("http_server")) { std::string http_addr = parameters["http_server"].get(); if (!webserver_already_set) webserver::handle_callback = [&file_sink]() { nlohmann::json stats; stats["written"] = file_sink->get_written(); stats["written_raw"] = file_sink->get_written_raw(); return stats.dump(4); }; logger->info("Start webserver on %s", http_addr.c_str()); webserver::start(http_addr); } // Attach signal signal(SIGINT, sig_handler_rec); signal(SIGTERM, sig_handler_rec); // Now, we wait uint64_t start_time = time(0); while (1) { uint64_t elapsed_time = time(0) - start_time; if (timeout > 0) { if (elapsed_time >= timeout) { logger->warn("Timeout is over! (%ds >= %ds) Stopping.", elapsed_time, timeout); break; } } if (rec_should_exit) { logger->warn("Signal Received. Stopping."); break; } if (int(elapsed_time) % 2 == 0) { if (parameters["baseband_format"].get() == "ziq") logger->info("Wrote %d MB, raw %d MB", int(file_sink->get_written() / 1e6), int(file_sink->get_written_raw() / 1e6)); else logger->info("Wrote %d MB", int(file_sink->get_written() / 1e6)); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); } // Stop recording file_sink->stop_recording(); // Stop cleanly source_ptr->stop(); if (parameters.contains("fft_enable")) { splitter->stop(); fft->stop(); } file_sink->stop(); if (parameters.contains("http_server")) webserver::stop(); return 0; }