Portapack application development tutorial (three) development steps

My thinking is like this. Since the application I developed is a copy of replay, I just need to copy a copy of the code I found earlier, modify it (renaming), and then add the added code file to the corresponding CMakeLists Just inside.

I took a few steps.

The first step is to add a button to the main interface of portapack, which is temporarily connected to replay.

The second step is to modify this button and change the icon (to change the icon, you also need to find pictures on the Internet, and then generate data).

The third step is to start to change the app interface corresponding to the new button on the interface, but this app also corresponds to the original background processing program.

The fourth step is to add the corresponding background processing program, so that the app of the new button on the interface has a completely independent background processing program.

 

first step:

Open ui_navigation.cpp, find the following part, and change it according to the comments.

SystemMenuView::SystemMenuView(NavigationView& nav) {
	add_items({
		{ "Play dead",				ui::Color::red(),		&bitmap_icon_playdead,	[&nav](){ nav.push<PlayDeadView>(); } },
		{ "Receivers", 	ui::Color::dark_cyan(),		&bitmap_icon_receivers,	[&nav](){ nav.push<ReceiversMenuView>(); } },
		{ "Transmit", 	ui::Color::green(),			&bitmap_icon_transmit,	[&nav](){ nav.push<TransmittersMenuView>(); } },
		{ "Capture",				ui::Color::blue(),		&bitmap_icon_capture,	[&nav](){ nav.push<CaptureAppView>(); } },
		{ "Replay",					ui::Color::purple(),	&bitmap_icon_replay,	[&nav](){ nav.push<ReplayAppView>(); } },
                { "GPS Sim",                              ui::Color::white(),     &bitmap_icon_replay,    [&nav]() { nav.push<ReplayAppView>(); } },
 //这一行是我们自己增加的,只改了名字和颜色
		{ "Calls",		ui::Color::yellow(),	    &bitmap_icon_closecall,	[&nav](){ nav.push<SearchView>(); } },
		{ "Scanner",	ui::Color::orange(),		&bitmap_icon_scanner,	[&nav](){ nav.push<ScannerView>(); } },
		{ "Utilities",				ui::Color::light_grey(),	&bitmap_icon_utilities,	[&nav](){ nav.push<UtilitiesMenuView>(); } },
		{ "Settings", 	ui::Color::cyan(),			&bitmap_icon_setup,	  	[&nav](){ nav.push<SettingsMenuView>(); } },
		//{ "Debug",		ui::Color::cyan(),			nullptr,   				[&nav](){ nav.push<DebugMenuView>(); } },
		{ "HackRF", 	ui::Color::cyan(),			&bitmap_icon_hackrf,	[this, &nav](){ hackrf_mode(nav); } },
		{ "About", 		ui::Color::cyan(),			nullptr,				[&nav](){ nav.push<AboutView>(); } }
	});
	
	set_highlighted(1);		// Startup selection is "Receivers"
}

The original main interface: 

After the modification, you can see the main interface like this:

 

The second step:

Still open ui_navigation.cpp and find the corresponding location change according to the comment.

SystemMenuView::SystemMenuView(NavigationView& nav) {
	add_items({
		{ "Play dead",				ui::Color::red(),		&bitmap_icon_playdead,	[&nav](){ nav.push<PlayDeadView>(); } },
		{ "Receivers", 	ui::Color::dark_cyan(),		&bitmap_icon_receivers,	[&nav](){ nav.push<ReceiversMenuView>(); } },
		{ "Transmit", 	ui::Color::green(),			&bitmap_icon_transmit,	[&nav](){ nav.push<TransmittersMenuView>(); } },
		{ "Capture",				ui::Color::blue(),		&bitmap_icon_capture,	[&nav](){ nav.push<CaptureAppView>(); } },
		{ "Replay",					ui::Color::purple(),	&bitmap_icon_replay,	[&nav](){ nav.push<ReplayAppView>(); } },
                { "GPS Sim",                              ui::Color::white(),     &bitmap_gps_sim,    [&nav]()
{ nav.push< ReplayAppView >(); } },
//我们现在把图标从bitmap_icon_replay改为bitmap_gps_sim
		{ "Calls",		ui::Color::yellow(),	    &bitmap_icon_closecall,	[&nav](){ nav.push<SearchView>(); } },
		{ "Scanner",	ui::Color::orange(),		&bitmap_icon_scanner,	[&nav](){ nav.push<ScannerView>(); } },
		{ "Utilities",				ui::Color::light_grey(),	&bitmap_icon_utilities,	[&nav](){ nav.push<UtilitiesMenuView>(); } },
		{ "Settings", 	ui::Color::cyan(),			&bitmap_icon_setup,	  	[&nav](){ nav.push<SettingsMenuView>(); } },
		//{ "Debug",		ui::Color::cyan(),			nullptr,   				[&nav](){ nav.push<DebugMenuView>(); } },
		{ "HackRF", 	ui::Color::cyan(),			&bitmap_icon_hackrf,	[this, &nav](){ hackrf_mode(nav); } },
		{ "About", 		ui::Color::cyan(),			nullptr,				[&nav](){ nav.push<AboutView>(); } }
	});
	
	set_highlighted(1);		// Startup selection is "Receivers"
}

But now bitmap_gps_sim is not available. If we compile now, an error will be reported. I searched it, and the bitmap_icon_replay replaced by it appeared in bitmap.hpp. The fragment is as follows:

static constexpr uint8_t bitmap_icon_replay_data[] = {
	0x00, 0x00, 
	0xC0, 0x07, 
	0xF0, 0x1F, 
	0x79, 0x3C, 
	0x1D, 0x70, 
	0x0F, 0x60, 
	0x07, 0xE0, 
	0x1F, 0xC0, 
	0x00, 0xC0, 
	0x00, 0xE0, 
	0x00, 0x60, 
	0x00, 0x70, 
	0x30, 0x3C, 
	0xE0, 0x0F, 
	0x80, 0x03, 
	0x00, 0x00, 
};
static constexpr Bitmap bitmap_icon_replay {
	{ 16, 16 }, bitmap_icon_replay_data
};

In other words, we have to find a way to generate such data from the icon, and then fill it in in this format.

I found firmware/tools/make_bitmap.py. According to the requirements of the code, I just need to find a png image whose width and height can be divisible by 8, put it in a folder, and run:

python make_bitmap.py folder name

Then you can generate the corresponding bitmap.hpp file, and then copy the required part of the code to the original bitmap.hpp.

I found the following icon

Then the data is generated:

static constexpr uint8_t bitmap_gps_sim_data[] = {
	0x00, 0x00, 
	0x00, 0x00, 
	0x40, 0x06, 
	0x20, 0x00, 
	0x10, 0x10, 
	0x08, 0x10, 
	0x00, 0x20, 
	0x00, 0x00, 
	0x08, 0x00, 
	0x08, 0x00, 
	0x08, 0x10, 
	0x10, 0x00, 
	0x40, 0x0C, 
	0xC0, 0x02, 
	0x00, 0x00, 
	0x00, 0x00, 
};
static constexpr Bitmap bitmap_gps_sim {
	{ 16, 16 }, bitmap_gps_sim_data
};

 Note that the file name of the icon corresponds to the variable name of the generated code. This name will also be used in the ui_navigation code afterwards, and it must be consistent, otherwise this data will not be found.

After the change is completed, the main interface will become like this:

But the interface that comes out after clicking GPS Sim is the same as the interface of clicking replay.

third step:

First, imitate replay_app.cpp and replay_app.hpp to generate gps_sim_app.cpp and gps_sim_app.hpp, and put them under firmware/applicaition/app.

gps_sim_app.cpp

#include "gps_sim_app.hpp"
#include "string_format.hpp"

#include "ui_fileman.hpp"
#include "io_file.hpp"

#include "baseband_api.hpp"
#include "portapack.hpp"
#include "portapack_persistent_memory.hpp"

using namespace portapack;

namespace ui {

void GpsSimAppView::set_ready() {
	ready_signal = true;
}

void GpsSimAppView::on_file_changed(std::filesystem::path new_file_path) {
	File data_file, info_file;
	char file_data[257];
	
	// Get file size
	auto data_open_error = data_file.open("/" + new_file_path.string());
	if (data_open_error.is_valid()) {
		file_error();
		return;
	}
	
	file_path = new_file_path;
	
	// Get original record frequency if available
	std::filesystem::path info_file_path = file_path;
	info_file_path.replace_extension(u".TXT");
	
	sample_rate = 500000;
	
	auto info_open_error = info_file.open("/" + info_file_path.string());
	if (!info_open_error.is_valid()) {
		memset(file_data, 0, 257);
		auto read_size = info_file.read(file_data, 256);
		if (!read_size.is_error()) {
			auto pos1 = strstr(file_data, "center_frequency=");
			if (pos1) {
				pos1 += 17;
				field_frequency.set_value(strtoll(pos1, nullptr, 10));
			}
			
			auto pos2 = strstr(file_data, "sample_rate=");
			if (pos2) {
				pos2 += 12;
				sample_rate = strtoll(pos2, nullptr, 10);
			}
		}
	}
	
	text_sample_rate.set(unit_auto_scale(sample_rate, 3, 1) + "Hz");
	
	auto file_size = data_file.size();
	auto duration = (file_size * 1000) / (1 * 2 * sample_rate);
	
	progressbar.set_max(file_size);
	text_filename.set(file_path.filename().string().substr(0, 12));
	text_duration.set(to_string_time_ms(duration));
	
	button_play.focus();
}

void GpsSimAppView::on_tx_progress(const uint32_t progress) {
	progressbar.set_value(progress);
}

void GpsSimAppView::focus() {
	button_open.focus();
}

void GpsSimAppView::file_error() {
	nav_.display_modal("Error", "File read error.");
}

bool GpsSimAppView::is_active() const {
	return (bool)replay_thread;
}

void GpsSimAppView::toggle() {
	if( is_active() ) {
		stop(false);
	} else {
		start();
	}
}

void GpsSimAppView::start() {
	stop(false);

	std::unique_ptr<stream::Reader> reader;
	
	auto p = std::make_unique<FileReader>();
	auto open_error = p->open(file_path);
	if( open_error.is_valid() ) {
		file_error();
	} else {
		reader = std::move(p);
	}

	if( reader ) {
		button_play.set_bitmap(&bitmap_stop);
		baseband::set_sample_rate(sample_rate * 4);
		
		replay_thread = std::make_unique<ReplayThread>(
			std::move(reader),
			read_size, buffer_count,
			&ready_signal,
			[](uint32_t return_code) {
				ReplayThreadDoneMessage message { return_code };
				EventDispatcher::send_message(message);
			}
		);
	}
	
	radio::enable({
		receiver_model.tuning_frequency(),
		sample_rate * 4,
		baseband_bandwidth,
		rf::Direction::Transmit,
		receiver_model.rf_amp(),
		static_cast<int8_t>(receiver_model.lna()),
		static_cast<int8_t>(receiver_model.vga())
	});
}

void GpsSimAppView::stop(const bool do_loop) {
	if( is_active() )
		replay_thread.reset();
	
	if (do_loop && check_loop.value()) {
		start();
	} else {
		radio::disable();
		button_play.set_bitmap(&bitmap_play);
	}
	
	ready_signal = false;
}

void GpsSimAppView::handle_replay_thread_done(const uint32_t return_code) {
	if (return_code == ReplayThread::END_OF_FILE) {
		stop(true);
	} else if (return_code == ReplayThread::READ_ERROR) {
		stop(false);
		file_error();
	}
	
	progressbar.set_value(0);
}

GpsSimAppView::GpsSimAppView(
	NavigationView& nav
) : nav_ (nav)
{
	baseband::run_image(portapack::spi_flash::image_tag_replay);

	add_children({
		&labels,
		&button_open,
		&text_filename,
		&text_sample_rate,
		&text_duration,
		&progressbar,
		&field_frequency,
		&field_lna,
		&field_rf_amp,
		&check_loop,
		&button_play,
		&waterfall,
	});
	
	field_frequency.set_value(target_frequency());
	field_frequency.set_step(receiver_model.frequency_step());
	field_frequency.on_change = [this](rf::Frequency f) {
		this->on_target_frequency_changed(f);
	};
	field_frequency.on_edit = [this, &nav]() {
		// TODO: Provide separate modal method/scheme?
		auto new_view = nav.push<FrequencyKeypadView>(this->target_frequency());
		new_view->on_changed = [this](rf::Frequency f) {
			this->on_target_frequency_changed(f);
			this->field_frequency.set_value(f);
		};
	};

	field_frequency.set_step(5000);
	
	button_play.on_select = [this](ImageButton&) {
		this->toggle();
	};
	
	button_open.on_select = [this, &nav](Button&) {
		auto open_view = nav.push<FileLoadView>(".C8");
		open_view->on_changed = [this](std::filesystem::path new_file_path) {
			on_file_changed(new_file_path);
		};
	};
}

GpsSimAppView::~GpsSimAppView() {
	radio::disable();
	baseband::shutdown();
}

void GpsSimAppView::on_hide() {
	// TODO: Terrible kludge because widget system doesn't notify Waterfall that
	// it's being shown or hidden.
	waterfall.on_hide();
	View::on_hide();
}

void GpsSimAppView::set_parent_rect(const Rect new_parent_rect) {
	View::set_parent_rect(new_parent_rect);

	const ui::Rect waterfall_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height };
	waterfall.set_parent_rect(waterfall_rect);
}

void GpsSimAppView::on_target_frequency_changed(rf::Frequency f) {
	set_target_frequency(f);
}

void GpsSimAppView::set_target_frequency(const rf::Frequency new_value) {
	persistent_memory::set_tuned_frequency(new_value);;
}

rf::Frequency GpsSimAppView::target_frequency() const {
	return persistent_memory::tuned_frequency();
}

} /* namespace ui */

gps_sim_app.hpp


#ifndef __GPS_SIM_APP_HPP__
#define __GPS_SIM_APP_HPP__

#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "replay_thread.hpp"
#include "ui_spectrum.hpp"

#include <string>
#include <memory>

namespace ui {

class GpsSimAppView : public View {
public:
	GpsSimAppView(NavigationView& nav);
	~GpsSimAppView();

	void on_hide() override;
	void set_parent_rect(const Rect new_parent_rect) override;
	void focus() override;

	std::string title() const override { return "GPS Simulator"; };
	
private:
	NavigationView& nav_;
	
	static constexpr ui::Dim header_height = 3 * 16;
	
	uint32_t sample_rate = 0;
	static constexpr uint32_t baseband_bandwidth = 2000000;
	const size_t read_size { 16384 };
	const size_t buffer_count { 3 };

	void on_file_changed(std::filesystem::path new_file_path);
	void on_target_frequency_changed(rf::Frequency f);
	void on_tx_progress(const uint32_t progress);
	
	void set_target_frequency(const rf::Frequency new_value);
	rf::Frequency target_frequency() const;

	void toggle();
	void start();
	void stop(const bool do_loop);
	bool is_active() const;
	void set_ready();
	void handle_replay_thread_done(const uint32_t return_code);
	void file_error();

	std::filesystem::path file_path { };
	std::unique_ptr<ReplayThread> replay_thread { };
	bool ready_signal { false };

	Labels labels {
		{ { 10 * 8, 2 * 16 }, "LNA:   A:", Color::light_grey() }
	};
	
	Button button_open {
		{ 0 * 8, 0 * 16, 10 * 8, 2 * 16 },
		"Open file"
	};
	
	Text text_filename {
		{ 11 * 8, 0 * 16, 12 * 8, 16 },
		"-"
	};
	Text text_sample_rate {
		{ 24 * 8, 0 * 16, 6 * 8, 16 },
		"-"
	};
	
	Text text_duration {
		{ 11 * 8, 1 * 16, 6 * 8, 16 },
		"-"
	};
	ProgressBar progressbar {
		{ 18 * 8, 1 * 16, 12 * 8, 16 }
	};
	
	FrequencyField field_frequency {
		{ 0 * 8, 2 * 16 },
	};
	LNAGainField field_lna {
		{ 14 * 8, 2 * 16 }
	};
	RFAmpField field_rf_amp {
		{ 19 * 8, 2 * 16 }
	};
	Checkbox check_loop {
		{ 21 * 8, 2 * 16 },
		4,
		"Loop",
		true
	};
	ImageButton button_play {
		{ 28 * 8, 2 * 16, 2 * 8, 1 * 16 },
		&bitmap_play,
		Color::green(),
		Color::black()
	};

	spectrum::WaterfallWidget waterfall { };

	MessageHandlerRegistration message_handler_replay_thread_error {
		Message::ID::ReplayThreadDone,
		[this](const Message* const p) {
			const auto message = *reinterpret_cast<const ReplayThreadDoneMessage*>(p);
			this->handle_replay_thread_done(message.return_code);
		}
	};
	
	MessageHandlerRegistration message_handler_fifo_signal {
		Message::ID::RequestSignal,
		[this](const Message* const p) {
			const auto message = static_cast<const RequestSignalMessage*>(p);
			if (message->signal == RequestSignalMessage::Signal::FillRequest) {
				this->set_ready();
			}
		}
	};
	
	MessageHandlerRegistration message_handler_tx_progress {
		Message::ID::TXProgress,
		[this](const Message* const p) {
			const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
			this->on_tx_progress(message.progress);
		}
	};
};

} /* namespace ui */

#endif/*__GPS_SIM_APP_HPP__*/

Then find apps/replay_app.cpp in firmware/application/CMakeLists.txt, and insert apps/gps_sim_app.cpp below it. as follows:

	apps/replay_app.cpp
        apps/gps_sim_app.cpp

Then open ui_navigation.cpp and add #include "gps_sim_app.hpp" at the beginning

Then it is also the window in ui_navigation.cpp to change the nav.push call of GPS Sim

SystemMenuView::SystemMenuView(NavigationView& nav) {
	add_items({
		{ "Play dead",				ui::Color::red(),		&bitmap_icon_playdead,	[&nav](){ nav.push<PlayDeadView>(); } },
		{ "Receivers", 	ui::Color::dark_cyan(),		&bitmap_icon_receivers,	[&nav](){ nav.push<ReceiversMenuView>(); } },
		{ "Transmit", 	ui::Color::green(),			&bitmap_icon_transmit,	[&nav](){ nav.push<TransmittersMenuView>(); } },
		{ "Capture",				ui::Color::blue(),		&bitmap_icon_capture,	[&nav](){ nav.push<CaptureAppView>(); } },
		{ "Replay",					ui::Color::purple(),	&bitmap_icon_replay,	[&nav](){ nav.push<ReplayAppView>(); } },
                { "GPS Sim",                              ui::Color::white(),     &bitmap_gps_sim,    [&nav]()
{ nav.push<GpsSimAppView>(); } }, //注意nav.push这改为另一个界面了
		{ "Calls",		ui::Color::yellow(),	    &bitmap_icon_closecall,	[&nav](){ nav.push<SearchView>(); } },
		{ "Scanner",	ui::Color::orange(),		&bitmap_icon_scanner,	[&nav](){ nav.push<ScannerView>(); } },
		{ "Utilities",				ui::Color::light_grey(),	&bitmap_icon_utilities,	[&nav](){ nav.push<UtilitiesMenuView>(); } },
		{ "Settings", 	ui::Color::cyan(),			&bitmap_icon_setup,	  	[&nav](){ nav.push<SettingsMenuView>(); } },
		//{ "Debug",		ui::Color::cyan(),			nullptr,   				[&nav](){ nav.push<DebugMenuView>(); } },
		{ "HackRF", 	ui::Color::cyan(),			&bitmap_icon_hackrf,	[this, &nav](){ hackrf_mode(nav); } },
		{ "About", 		ui::Color::cyan(),			nullptr,				[&nav](){ nav.push<AboutView>(); } }
	});
	
	set_highlighted(1);		// Startup selection is "Receivers"
}

After the third step is completed, click GPS Sim and you will see an interface similar to the following. You can see that the title has changed. I also opened a GPS data file that I prepared before. The difference will be more obvious.

the fourth step:

In line 183 of gps_sim_app.cpp in the third step, image_tag_replay is changed to image_tag_gps.

	baseband::run_image(portapack::spi_flash::image_tag_gps);

In this way, this interface program will look for another background processing program when it is executed, but it cannot be found temporarily. We need to add two files, proc_gps_sim.cpp and proc_gps_sim.hpp, in firmware/baseband, imitating proc_replay.app and proc_replay.hpp. Then find spi_image.hpp in firmware/common/ and find it. image_tag_replay, imitate it to increase image_tag_gps.

constexpr image_tag_t image_tag_replay				{ 'P', 'R', 'E', 'P' };
constexpr image_tag_t image_tag_gps                             { 'P', 'G', 'P', 'S' };

So every time the interface program looks for image_tag_gps, it will look for PGPS. How does this PGPS correspond to proc_gps_sim.cpp? This is what we will do next.

Open firmware/baseband/CMakeLists.txt and find ###Replay

### Replay

set(MODE_CPPSRC
	proc_replay.cpp
)
DeclareTargets(PREP replay)

### GPS Simulator

set(MODE_CPPSRC
	proc_gps_sim.cpp
)
DeclareTargets(PGPS gps_sim)

Then imitate it and write the content behind ### GPS Simulator and see that there is PGPS in the DeclareTargets at the bottom, which means that every time PGPS is called, it will go to the program compiled by proc_gps_sim.cpp.

Finally, attach my current proc_gps_sim.cpp and proc_gps_sim.hpp

proc_gps_sim.cpp

#include "proc_gps_sim.hpp"
#include "sine_table_int8.hpp"
#include "portapack_shared_memory.hpp"

#include "event_m4.hpp"

#include "utility.hpp"

ReplayProcessor::ReplayProcessor() {
	channel_filter_pass_f = taps_200k_decim_1.pass_frequency_normalized * 1000000;	// 162760.416666667
	channel_filter_stop_f = taps_200k_decim_1.stop_frequency_normalized * 1000000;	// 337239.583333333
	
	spectrum_samples = 0;

	channel_spectrum.set_decimation_factor(1);
	
	configured = false;
}

void ReplayProcessor::execute(const buffer_c8_t& buffer) {
	/* 4MHz, 2048 samples */
	
	if (!configured) return;
	
	// File data is in C16 format, we need C8
	// File samplerate is 500kHz, we're at 4MHz
	// iq_buffer can only be 512 C16 samples (RAM limitation)
	// To fill up the 2048-sample C8 buffer, we need:
	// 2048 samples * 2 bytes per sample = 4096 bytes
	// Since we're oversampling by 4M/500k = 8, we only need 2048/8 = 256 samples from the file and duplicate them 8 times each
	// So 256 * 4 bytes per sample (C16) = 1024 bytes from the file
	if( stream ) {                             //sizeof(*buffer.p) = sizeof(C8) = 2*int8 = 2 bytes //buffer.count = 2048
		const size_t bytes_to_read = sizeof(*buffer.p) * 1 * (buffer.count / 8);	// *2 (C16), /8 (oversampling) should be == 1024
		bytes_read += stream->read(iq_buffer.p, bytes_to_read);
	}
	
	// Fill and "stretch"
	for (size_t i = 0; i < buffer.count; i++) {
		/*if (i & 3) {
			buffer.p[i] = buffer.p[i - 1];
		} else {
			auto re_out = iq_buffer.p[i >> 3].real() ;
			auto im_out = iq_buffer.p[i >> 3].imag() ;
			buffer.p[i] = { (int8_t)re_out, (int8_t)im_out };
		}*/
                if (i % 8 != 0) {
			buffer.p[i] = buffer.p[i - 1];
		} else {
			auto re_out = iq_buffer.p[i/8].real() ;
			auto im_out = iq_buffer.p[i/8].imag() ;
			buffer.p[i] = { (int8_t)re_out, (int8_t)im_out };
		}
                /*
                auto re_out = iq_buffer.p[i].real() ;
	        auto im_out = iq_buffer.p[i].imag() ;
		buffer.p[i] = { (int8_t)re_out, (int8_t)im_out };*/
	}
	
	spectrum_samples += buffer.count;
	if( spectrum_samples >= spectrum_interval_samples ) {
		spectrum_samples -= spectrum_interval_samples;
		//channel_spectrum.feed(iq_buffer, channel_filter_pass_f, channel_filter_stop_f);
		
		txprogress_message.progress = bytes_read;	// Inform UI about progress
		txprogress_message.done = false;
		shared_memory.application_queue.push(txprogress_message);
	}
}

void ReplayProcessor::on_message(const Message* const message) {
	switch(message->id) {
	case Message::ID::UpdateSpectrum:
	case Message::ID::SpectrumStreamingConfig:
		channel_spectrum.on_message(message);
		break;

	case Message::ID::SamplerateConfig:
		samplerate_config(*reinterpret_cast<const SamplerateConfigMessage*>(message));
		break;
	
	case Message::ID::ReplayConfig:
		configured = false;
		bytes_read = 0;
		replay_config(*reinterpret_cast<const ReplayConfigMessage*>(message));
		break;
		
	// App has prefilled the buffers, we're ready to go now
	case Message::ID::FIFOData:
		configured = true;
		break;

	default:
		break;
	}
}

void ReplayProcessor::samplerate_config(const SamplerateConfigMessage& message) {
	baseband_fs = message.sample_rate;
	baseband_thread.set_sampling_rate(baseband_fs);
	spectrum_interval_samples = baseband_fs / spectrum_rate_hz;
}

void ReplayProcessor::replay_config(const ReplayConfigMessage& message) {
	if( message.config ) {
		
		stream = std::make_unique<StreamOutput>(message.config);
		
		// Tell application that the buffers and FIFO pointers are ready, prefill
		shared_memory.application_queue.push(sig_message);
	} else {
		stream.reset();
	}
}

int main() {
	EventDispatcher event_dispatcher { std::make_unique<ReplayProcessor>() };
	event_dispatcher.run();
	return 0;
}

proc_gps_sim.hpp

#ifndef __PROC_GPS_SIM_HPP__
#define __PROC_GPS_SIM_HPP__

#include "baseband_processor.hpp"
#include "baseband_thread.hpp"

#include "spectrum_collector.hpp"

#include "stream_output.hpp"

#include <array>
#include <memory>

class ReplayProcessor : public BasebandProcessor {
public:
	ReplayProcessor();

	void execute(const buffer_c8_t& buffer) override;

	void on_message(const Message* const message) override;

private:
	size_t baseband_fs = 0;
	static constexpr auto spectrum_rate_hz = 50.0f;

	BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Transmit };

	std::array<complex8_t, 256> iq { };
	const buffer_c8_t iq_buffer {
		iq.data(),
		iq.size(),
		baseband_fs /4
	};
	
	uint32_t channel_filter_pass_f = 0;
	uint32_t channel_filter_stop_f = 0;

	std::unique_ptr<StreamOutput> stream { };

	SpectrumCollector channel_spectrum { };
	size_t spectrum_interval_samples = 0;
	size_t spectrum_samples = 0;
	
	bool configured { false };
	uint32_t bytes_read { 0 };

	void samplerate_config(const SamplerateConfigMessage& message);
	void replay_config(const ReplayConfigMessage& message);
	
	TXProgressMessage txprogress_message { };
	RequestSignalMessage sig_message { RequestSignalMessage::Signal::FillRequest };
};

#endif/*__PROC_GPS_SIM_HPP__*/

The next article will carefully analyze the 4 files I wrote gps_sim_app.cpp, gps_sim_app.hpp, proc_gps_sim.cpp, proc_gps_sim.hpp, and the problems I encountered.

These problems are related to GPS signal and iq data processing.

But the steps above can be regarded as the process of writing a new application.

Guess you like

Origin blog.csdn.net/shukebeta008/article/details/104308453