diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt
index 6d9251f58efe15c128936c14645907c784f6b6ac..41428aaf316dc584b39ac2dfd3b1779c4e79fc3b 100644
--- a/firmware/application/CMakeLists.txt
+++ b/firmware/application/CMakeLists.txt
@@ -238,6 +238,7 @@ set(CPPSRC
 	apps/ui_soundboard.cpp
 	apps/ui_sstvtx.cpp
 	apps/ui_test.cpp
+	apps/ui_tone_search.cpp
 	apps/ui_touch_calibration.cpp
 	apps/ui_touchtunes.cpp
 	apps/ui_view_wav.cpp
diff --git a/firmware/application/apps/ui_aprs_tx.cpp b/firmware/application/apps/ui_aprs_tx.cpp
index 257e892e270e937058afcc12c5fc3f03478fe9c8..63aa1cf98b4e30ae5d25080f5ed9d5d652029208 100644
--- a/firmware/application/apps/ui_aprs_tx.cpp
+++ b/firmware/application/apps/ui_aprs_tx.cpp
@@ -27,6 +27,7 @@
 #include "string_format.hpp"
 #include "portapack.hpp"
 #include "baseband_api.hpp"
+#include "portapack_shared_memory.hpp"
 #include "portapack_persistent_memory.hpp"
 
 #include <cstring>
@@ -46,21 +47,29 @@ APRSTXView::~APRSTXView() {
 	baseband::shutdown();
 }
 
-void APRSTXView::paint(Painter& painter) {
-	(void)painter;
-}
-
-void APRSTXView::generate_frame() {
-
-}
-
 void APRSTXView::start_tx() {
-	//generate_frame();
+	make_aprs_frame(
+		sym_source.value_string().c_str(), num_ssid_source.value(),
+		sym_dest.value_string().c_str(), num_ssid_dest.value(),
+		payload);
 	
-	/*transmitter_model.set_tuning_frequency(144800000);
+	//uint8_t * bb_data_ptr = shared_memory.bb_data.data;
+	//text_payload.set(to_string_hex_array(bb_data_ptr + 56, 15));
+	
+	transmitter_model.set_tuning_frequency(persistent_memory::tuned_frequency());
+	transmitter_model.set_sampling_rate(AFSK_TX_SAMPLERATE);
 	transmitter_model.set_rf_amp(true);
-	transmitter_model.set_baseband_bandwidth(2500000);
-	transmitter_model.enable();*/
+	transmitter_model.set_baseband_bandwidth(1750000);
+	transmitter_model.enable();
+	
+	baseband::set_afsk_data(
+		AFSK_TX_SAMPLERATE / 1200,
+		1200,
+		2200,
+		1,
+		10000,	//transmitter_model.channel_bandwidth(),
+		8
+	);
 }
 
 void APRSTXView::on_tx_progress(const uint32_t progress, const bool done) {
@@ -69,9 +78,6 @@ void APRSTXView::on_tx_progress(const uint32_t progress, const bool done) {
 	if (done) {
 		transmitter_model.disable();
 		tx_view.set_transmitting(false);
-		//progress.set_value(0);
-	} else {
-		//progress.set_value(n);
 	}
 }
 
@@ -81,10 +87,26 @@ APRSTXView::APRSTXView(NavigationView& nav) {
 
 	add_children({
 		&labels,
-		&text_frame_a,
+		&sym_source,
+		&num_ssid_source,
+		&sym_dest,
+		&num_ssid_dest,
+		&text_payload,
+		&button_set,
 		&tx_view
 	});
 	
+	button_set.on_select = [this, &nav](Button&) {
+		text_prompt(
+			nav,
+			&payload,
+			30,
+			[this](std::string* s) {
+				text_payload.set(*s);
+			}
+		);
+	};
+	
 	tx_view.on_edit_frequency = [this, &nav]() {
 		return;
 	};
diff --git a/firmware/application/apps/ui_aprs_tx.hpp b/firmware/application/apps/ui_aprs_tx.hpp
index d920684b9bd49aeeda521e55156501ede470c2ef..173d9fd703bf86f8d3e776ae8608917798ae72b0 100644
--- a/firmware/application/apps/ui_aprs_tx.hpp
+++ b/firmware/application/apps/ui_aprs_tx.hpp
@@ -38,8 +38,6 @@ public:
 	APRSTXView(NavigationView& nav);
 	~APRSTXView();
 	
-	void paint(Painter& painter) override;
-	
 	void focus() override;
 	
 	std::string title() const override { return "APRS TX (beta)"; };
@@ -53,25 +51,58 @@ private:
 	
 	tx_modes tx_mode = IDLE;*/
 	
+	std::string payload { "" };
+	
 	void start_tx();
 	void generate_frame();
 	void generate_frame_pos();
 	void on_tx_progress(const uint32_t progress, const bool done);
 	
 	Labels labels {
-		{ { 2 * 8, 2 * 8 }, "Work in progress...", Color::light_grey() }
+		{ { 0 * 8, 1 * 16 }, "Source:       SSID:", Color::light_grey() },	// 6 alphanum + SSID
+		{ { 0 * 8, 2 * 16 }, " Dest.:       SSID:", Color::light_grey() },
+		{ { 0 * 8, 4 * 16 }, "Info field:", Color::light_grey() },
+	};
+	
+	SymField sym_source {
+		{ 7 * 8, 1 * 16 },
+		6,
+		SymField::SYMFIELD_ALPHANUM
+	};
+	NumberField num_ssid_source {
+		{ 19 * 8, 1 * 16 },
+		2,
+		{ 0, 15 },
+		1,
+		' '
 	};
 	
-	Text text_frame_a {
-		{ 2 * 8, 13 * 16, 14 * 8, 16 },
+	SymField sym_dest {
+		{ 7 * 8, 2 * 16 },
+		6,
+		SymField::SYMFIELD_ALPHANUM
+	};
+	NumberField num_ssid_dest {
+		{ 19 * 8, 2 * 16 },
+		2,
+		{ 0, 15 },
+		1,
+		' '
+	};
+	
+	Text text_payload {
+		{ 0 * 8, 5 * 16, 30 * 8, 16 },
 		"-"
 	};
+	Button button_set {
+		{ 0 * 8, 6 * 16, 80, 32 },
+		"Set"
+	};
 	
 	TransmitterView tx_view {
 		16 * 16,
-		144800000,
-		2000000,
-		true
+		5000,
+		10
 	};
 	
 	MessageHandlerRegistration message_handler_tx_progress {
diff --git a/firmware/application/apps/ui_tone_search.cpp b/firmware/application/apps/ui_tone_search.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c7c2e0fc87a11ef04170f12aacb543d79a8f88be
--- /dev/null
+++ b/firmware/application/apps/ui_tone_search.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
+ * Copyright (C) 2018 Furrtek
+ *
+ * This file is part of PortaPack.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "ui_tone_search.hpp"
+
+#include "baseband_api.hpp"
+#include "string_format.hpp"
+
+using namespace portapack;
+
+namespace ui {
+
+void ToneSearchView::focus() {
+	//field_frequency_min.focus();
+}
+
+ToneSearchView::~ToneSearchView() {
+	receiver_model.disable();
+	baseband::shutdown();
+}
+
+ToneSearchView::ToneSearchView(
+	NavigationView& nav
+) : nav_ (nav)
+{
+	//baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
+	
+	add_children({
+		&labels
+	});
+}
+
+} /* namespace ui */
diff --git a/firmware/application/apps/ui_tone_search.hpp b/firmware/application/apps/ui_tone_search.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b32c9bff53d242bd260a0378d830e0156653a8f1
--- /dev/null
+++ b/firmware/application/apps/ui_tone_search.hpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
+ * Copyright (C) 2018 Furrtek
+ *
+ * This file is part of PortaPack.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "receiver_model.hpp"
+
+#include "ui_receiver.hpp"
+#include "ui_font_fixed_8x16.hpp"
+
+namespace ui {
+
+class ToneSearchView : public View {
+public:
+	ToneSearchView(NavigationView& nav);
+	~ToneSearchView();
+	
+	void focus() override;
+	
+	std::string title() const override { return "Tone search"; };
+
+private:
+	NavigationView& nav_;
+	
+	Labels labels {
+		{ { 1 * 8, 0 }, "Min:      Max:       LNA VGA", Color::light_grey() }
+	};
+	
+	/*
+	MessageHandlerRegistration message_handler_frame_sync {
+		Message::ID::DisplayFrameSync,
+		[this](const Message* const) {
+			if( this->fifo ) {
+				ChannelSpectrum channel_spectrum;
+				while( fifo->out(channel_spectrum) ) {
+					this->on_channel_spectrum(channel_spectrum);
+				}
+			}
+			this->do_timers();
+		}
+	};*/
+};
+
+} /* namespace ui */
diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp
index ac6244da9127232661b15ec3253b6b875f49d890..6d15d483665f40beaf197df9a9b016de6b555dfd 100755
--- a/firmware/application/main.cpp
+++ b/firmware/application/main.cpp
@@ -23,6 +23,7 @@
 // Color bitmaps generated with:
 // Gimp image > indexed colors (16), then "xxd -i *.bmp"
 
+//TEST: Goertzel
 //TEST: Menuview refresh, seems to blink a lot
 //TEST: Check AFSK transmit end, skips last bits ?
 //TEST: Imperial in whipcalc
@@ -44,7 +45,6 @@
 //TODO: De bruijn sequence scanner for encoders
 //TODO: FILEMAN Move files
 //TODO: Use separate thread for scanning in EPAR TX
-//TODO: Use separate thread for scanning in LCR TX
 //TODO: Make freqman refresh simpler (use previous black rectangle method)
 //TODO: Merge AFSK and TONES procs ?
 //TODO: NFM RX mode: nav.pop on squelch
diff --git a/firmware/application/protocols/aprs.cpp b/firmware/application/protocols/aprs.cpp
index d0d32636982d9ae496d2e3fabd83c2d0d1df46a2..96cf8df6fc0be80138e14deb2e5decb0f5540a2b 100644
--- a/firmware/application/protocols/aprs.cpp
+++ b/firmware/application/protocols/aprs.cpp
@@ -29,17 +29,20 @@ using namespace ax25;
 
 namespace aprs {
 
-void make_aprs_frame(char * address_dest, char * address_src) {
+void make_aprs_frame(const char * src_address, const uint32_t src_ssid,
+	const char * dest_address, const uint32_t dest_ssid,
+	const std::string& payload) {
+	
 	AX25Frame frame;
 	
 	char address[14] = { 0 };
-	uint8_t info[7] = { 0 };	//{ 'F','U','R','R','T','E','K' };
 	
-	// Both SSIDs are 0
-	memcpy(&address[0], address_dest, 6);
-	memcpy(&address[7], address_src, 6);
+	memcpy(&address[0], dest_address, 6);
+	address[6] = (dest_ssid & 15) << 1;
+	memcpy(&address[7], src_address, 6);
+	address[13] = (src_ssid & 15) << 1;
 	
-	frame.make_ui_frame(address, 0x03, protocol_id_t::NO_LAYER3, info, sizeof(info));
+	frame.make_ui_frame(address, 0x03, protocol_id_t::NO_LAYER3, payload);
 }
 
 } /* namespace aprs */
diff --git a/firmware/application/protocols/aprs.hpp b/firmware/application/protocols/aprs.hpp
index 79b3a6a6ff08db4d88bc000c712888667967ccd6..4229b9e51cfeed9b2f09905202f35cd1b2c4f94e 100644
--- a/firmware/application/protocols/aprs.hpp
+++ b/firmware/application/protocols/aprs.hpp
@@ -28,7 +28,10 @@
 
 namespace aprs {
 
-	void make_aprs_frame();
+	void make_aprs_frame(
+		const char * src_address, const uint32_t src_ssid,
+		const char * dest_address, const uint32_t dest_ssid,
+		const std::string& payload);
 
 } /* namespace aprs */
 
diff --git a/firmware/application/protocols/ax25.cpp b/firmware/application/protocols/ax25.cpp
index e784e7dd9baea59c891f67dba1f3009f3946468e..ea1744f6d8940da8f0114dba0a219c20cf54567f 100644
--- a/firmware/application/protocols/ax25.cpp
+++ b/firmware/application/protocols/ax25.cpp
@@ -35,39 +35,42 @@ void AX25Frame::make_extended_field(char * const data, size_t length) {
 	add_data((data[i] << 1) | 1);
 }
 
+void AX25Frame::NRZI_add_bit(const uint32_t bit) {
+	if (!bit)
+		current_bit ^= 1;		// Zero: flip
+	
+	current_byte <<= 1;
+	current_byte |= current_bit;
+	
+	bit_counter++;
+	
+	if (bit_counter == 8) {
+		bit_counter = 0;
+		*bb_data_ptr = current_byte;
+		bb_data_ptr++;
+	}
+}
+
 void AX25Frame::add_byte(uint8_t byte, bool is_flag, bool is_data) {
-	size_t i;
+	uint32_t bit;
+	
+	if (is_data)
+		crc_ccitt.process_byte(byte);
 	
-	for (i = 0; i < 8; ) {
+	for (uint32_t i = 0; i < 8; i++) {
+		bit = (byte >> i) & 1;
 		
-		if (!(byte & 1)) {
-			current_bit ^= 1;		// Zero: flip
-			ones_counter = 0;
-			byte >>= 1;
-			i++;
-		} else {
+		if (bit)
 			ones_counter++;
-			if ((ones_counter == 5) && (!is_flag)) {
-				current_bit ^= 1;	// Stuff zero: flip
-				ones_counter = 0;
-			} else {
-				byte >>= 1;
-				i++;
-			}
-		}
-		
-		if (is_data)
-			crc_ccitt.process_bit(current_bit);
-		current_byte <<= 1;
-		current_byte |= current_bit;
+		else
+			ones_counter = 0;
 		
-		if (bit_counter == 7) {
-			bit_counter = 0;
-			*bb_data_ptr = current_byte;
-			bb_data_ptr++;
-		} else {
-			bit_counter++;
+		if ((ones_counter == 6) && (!is_flag)) {
+			NRZI_add_bit(0);
+			ones_counter = 0;
 		}
+		
+		NRZI_add_bit(bit);
 	}
 }
 
@@ -86,32 +89,38 @@ void AX25Frame::add_data(uint8_t byte) {
 
 void AX25Frame::add_checksum() {
 	auto checksum = crc_ccitt.checksum();
-	add_byte(checksum >> 8, false, false);
 	add_byte(checksum, false, false);
+	add_byte(checksum >> 8, false, false);
 }
 
 void AX25Frame::make_ui_frame(char * const address, const uint8_t control,
-	const uint8_t protocol, uint8_t * const info, size_t length) {
+	const uint8_t protocol, const std::string& info) {
 	
 	size_t i;
 	
-	bb_data_ptr = shared_memory.bb_data.data;
+	bb_data_ptr = (uint16_t*)shared_memory.bb_data.data;
+	memset(bb_data_ptr, 0, sizeof(shared_memory.bb_data.data));
 	bit_counter = 0;
 	current_bit = 0;
 	current_byte = 0;
 	ones_counter = 0;
 	crc_ccitt.reset();
 	
+	add_flag();
+	add_flag();
+	add_flag();
 	add_flag();
 	
 	make_extended_field(address, 14);
 	add_data(control);
 	add_data(protocol);
 	
-	for (i = 0; i < length; i++)
+	for (i = 0; i < info.size(); i++)
 		add_data(info[i]);
 	
 	add_checksum();
+	
+	add_flag();
 	add_flag();
 	
 	flush();
diff --git a/firmware/application/protocols/ax25.hpp b/firmware/application/protocols/ax25.hpp
index badf51e215d046f912cb77ce620695956f0fd9fc..8819cf5a7bd738b80f59a683f505b09139d71045 100644
--- a/firmware/application/protocols/ax25.hpp
+++ b/firmware/application/protocols/ax25.hpp
@@ -44,9 +44,10 @@ enum protocol_id_t {
 class AX25Frame {
 public:
 	void make_ui_frame(char * const address, const uint8_t control, const uint8_t protocol,
-						uint8_t * const info, size_t length);
+						const std::string& info);
 	
 private:
+	void NRZI_add_bit(const uint32_t bit);
 	void make_extended_field(char * const data, size_t length);
 	void add_byte(uint8_t byte, bool is_flag, bool is_data);
 	void add_data(uint8_t byte);
@@ -54,13 +55,13 @@ private:
 	void add_flag();
 	void flush();
 	
-	uint8_t * bb_data_ptr { nullptr };
+	uint16_t * bb_data_ptr { nullptr };
 	uint8_t current_bit { 0 };
 	uint8_t current_byte { 0 };
 	size_t bit_counter { 0 };
 	uint8_t ones_counter { 0 };
 	
-	CRC<16> crc_ccitt { 0x1021, 0xFFFF };
+	CRC<16, true, true> crc_ccitt { 0x1021, 0xFFFF, 0xFFFF };
 };
 
 } /* namespace ax25 */
diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp
index a166558fd67e13683a50d6bc38c42492058dbb2d..37aca0b878bd5ab90bb86cfe5394babe7062a503 100644
--- a/firmware/application/ui_navigation.cpp
+++ b/firmware/application/ui_navigation.cpp
@@ -59,6 +59,7 @@
 #include "ui_soundboard.hpp"
 #include "ui_sstvtx.hpp"
 #include "ui_test.hpp"
+#include "ui_tone_search.hpp"
 #include "ui_touchtunes.hpp"
 #include "ui_view_wav.hpp"
 #include "ui_whipcalc.hpp"
@@ -349,7 +350,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
 TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
 	add_items({
 		{ "ADS-B Mode S", 			ui::Color::yellow(), 	&bitmap_icon_adsb,		[&nav](){ nav.push<ADSBTxView>(); } },
-		{ "APRS", 					ui::Color::grey(),		&bitmap_icon_aprs,		[&nav](){ nav.push<APRSTXView>(); } },
+		{ "APRS", 					ui::Color::orange(),	&bitmap_icon_aprs,		[&nav](){ nav.push<APRSTXView>(); } },
 		{ "BHT Xy/EP", 				ui::Color::green(), 	&bitmap_icon_bht,		[&nav](){ nav.push<BHTView>(); } },
 		{ "Jammer", 				ui::Color::yellow(),	&bitmap_icon_jammer,	[&nav](){ nav.push<JammerView>(); } },
 		{ "Key fob", 				ui::Color::orange(),	&bitmap_icon_keyfob,	[&nav](){ nav.push<KeyfobView>(); } },
@@ -360,11 +361,10 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
 		{ "Generic OOK remotes", 	ui::Color::yellow(),	&bitmap_icon_remote,	[&nav](){ nav.push<EncodersView>(); } },
 		{ "POCSAG", 				ui::Color::green(),		&bitmap_icon_pocsag,	[&nav](){ nav.push<POCSAGTXView>(); } },
 		{ "RDS",					ui::Color::green(),		&bitmap_icon_rds,		[&nav](){ nav.push<RDSView>(); } },
-		{ "Signal generator", 		ui::Color::green(), 	&bitmap_icon_cwgen,		[&nav](){ nav.push<SigGenView>(); } },
 		{ "Soundboard", 			ui::Color::green(), 	&bitmap_icon_soundboard,	[&nav](){ nav.push<SoundBoardView>(); } },
 		{ "SSTV", 					ui::Color::green(), 	&bitmap_icon_sstv,		[&nav](){ nav.push<SSTVTXView>(); } },
 		{ "TEDI/LCR AFSK", 			ui::Color::yellow(), 	&bitmap_icon_lcr,		[&nav](){ nav.push<LCRView>(); } },
-		{ "TouchTunes remote",		ui::Color::orange(),	nullptr,				[&nav](){ nav.push<TouchTunesView>(); } },
+		{ "TouchTunes remote",		ui::Color::yellow(),	nullptr,				[&nav](){ nav.push<TouchTunesView>(); } },
 	});
 	on_left = [&nav](){ nav.pop(); };
 }
@@ -376,8 +376,10 @@ UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav) {
 		{ "Test app", 				ui::Color::grey(), 		nullptr,				[&nav](){ nav.push<TestView>(); } },
 		{ "Frequency manager", 		ui::Color::green(), 	&bitmap_icon_freqman,	[&nav](){ nav.push<FrequencyManagerView>(); } },
 		{ "File manager", 			ui::Color::yellow(),	&bitmap_icon_file,		[&nav](){ nav.push<FileManagerView>(); } },
-		{ "Whip antenna length",	ui::Color::yellow(),	nullptr,				[&nav](){ nav.push<WhipCalcView>(); } },
 		{ "Notepad",				ui::Color::grey(),		&bitmap_icon_notepad,	[&nav](){ nav.push<NotImplementedView>(); } },
+		{ "Signal generator", 		ui::Color::green(), 	&bitmap_icon_cwgen,		[&nav](){ nav.push<SigGenView>(); } },
+		{ "Tone search", 			ui::Color::grey(), 		nullptr,				[&nav](){ nav.push<NotImplementedView>(); } },	// ToneSearchView
+		{ "Whip antenna length",	ui::Color::yellow(),	nullptr,				[&nav](){ nav.push<WhipCalcView>(); } },
 		{ "Wipe SD card",			ui::Color::red(),		nullptr,				[&nav](){ nav.push<WipeSDView>(); } },
 	});
 	on_left = [&nav](){ nav.pop(); };
diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt
index 9ca339fdd1fb3597640d9974396839f616c2e282..13f0a62fefed00bbc6333d02cd589ac9fb119842 100644
--- a/firmware/baseband/CMakeLists.txt
+++ b/firmware/baseband/CMakeLists.txt
@@ -113,6 +113,7 @@ set(CPPSRC
 	baseband_stats_collector.cpp
 	dsp_decimate.cpp
 	dsp_demodulate.cpp
+	dsp_goertzel.cpp
 	matched_filter.cpp
 	spectrum_collector.cpp
 	stream_input.cpp
diff --git a/firmware/baseband/dsp_goertzel.cpp b/firmware/baseband/dsp_goertzel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eaa374d055170eecad6e7aee034d8fbf0788f8b0
--- /dev/null
+++ b/firmware/baseband/dsp_goertzel.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
+ * Copyright (C) 2018 Furrtek
+ *
+ * This file is part of PortaPack.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "dsp_goertzel.hpp"
+
+#include "complex.hpp"
+#include "sine_table.hpp"
+
+namespace dsp {
+
+GoertzelDetector::GoertzelDetector(
+	const float frequency,
+	const uint32_t sample_rate
+) {
+	coefficient = 2.0 * sin_f32((2.0 * pi * frequency / sample_rate) - pi / 2.0);
+}
+
+float GoertzelDetector::execute(
+	const buffer_s16_t& src
+) {
+
+	const size_t count = src.count;
+	
+	for (size_t i = 0; i < count; i++) {
+		s[2] = s[1];
+		s[1] = s[0];
+		s[0] = src.p[i] + coefficient * s[1] - s[2];
+	}
+
+	const uint32_t sq0 = s[0] * s[0];
+	const uint32_t sq1 = s[1] * s[1];
+	float magnitude = __builtin_sqrtf(sq0 + sq1 - s[0] * s[1] * coefficient);
+
+	return magnitude;
+}
+
+} /* namespace dsp */
diff --git a/firmware/baseband/dsp_goertzel.hpp b/firmware/baseband/dsp_goertzel.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7b733a0e7df7f65d531453048a0b2e2202cfcd0a
--- /dev/null
+++ b/firmware/baseband/dsp_goertzel.hpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
+ * Copyright (C) 2018 Furrtek
+ *
+ * This file is part of PortaPack.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __DSP_GOERTZEL_H__
+#define __DSP_GOERTZEL_H__
+
+#include "dsp_types.hpp"
+
+namespace dsp {
+
+class GoertzelDetector {
+public:
+	GoertzelDetector(const float frequency, const uint32_t sample_rate);
+	
+	float execute(const buffer_s16_t& src);
+
+private:
+	float coefficient { };
+	int16_t s[2] { 0 };
+};
+
+} /* namespace dsp */
+
+#endif/*__DSP_GOERTZEL_H__*/
diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp
index d1e8044fe31a23e0a92c072dd3264c2c949c4c89..756ac5133d64a5f1ceae0ec67fb3c492db63d463 100644
--- a/firmware/common/ui_widget.cpp
+++ b/firmware/common/ui_widget.cpp
@@ -1309,17 +1309,19 @@ SymField::SymField(
 	} else if (type == SYMFIELD_HEX) {
 		for (c = 0; c < length; c++)
 			set_symbol_list(c, "0123456789ABCDEF");
+	} else if (type == SYMFIELD_ALPHANUM) {
+		for (c = 0; c < length; c++)
+			set_symbol_list(c, " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
 	}
 	
 	set_focusable(true);
 }
 
 uint32_t SymField::value_dec_u32() {
-	uint32_t c, mul = 1;
-	uint32_t v = 0;
+	uint32_t mul = 1, v = 0;
 	
 	if (type_ == SYMFIELD_DEC) {
-		for (c = 0; c < length_; c++) {
+		for (uint32_t c = 0; c < length_; c++) {
 			v += values_[(length_ - 1 - c)] * mul;
 			mul *= 10;
 		}
@@ -1329,16 +1331,27 @@ uint32_t SymField::value_dec_u32() {
 }
 
 uint64_t SymField::value_hex_u64() {
-	uint32_t c;
 	uint64_t v = 0;
 	
 	if (type_ != SYMFIELD_DEF) {
-		for (c = 0; c < length_; c++)
+		for (uint32_t c = 0; c < length_; c++)
 			v += (uint64_t)(values_[c]) << (4 * (length_ - 1 - c));
 		return v;
 	} else 
 		return 0;
 }
+
+std::string SymField::value_string() {
+	std::string return_string { "" };
+	
+	if (type_ == SYMFIELD_ALPHANUM) {
+		for (uint32_t c = 0; c < length_; c++) {
+			return_string += symbol_list_[0][values_[c]];
+		}
+	}
+	
+	return return_string;
+}
 	
 uint32_t SymField::get_sym(const uint32_t index) {
 	if (index >= length_) return 0;
diff --git a/firmware/common/ui_widget.hpp b/firmware/common/ui_widget.hpp
index 7874d44825e3058dd576bc177ce4c70dd581da4e..49b40bd137c23168c65f987bd16e8209bcc262f3 100644
--- a/firmware/common/ui_widget.hpp
+++ b/firmware/common/ui_widget.hpp
@@ -563,6 +563,7 @@ public:
 		SYMFIELD_OCT,
 		SYMFIELD_DEC,
 		SYMFIELD_HEX,
+		SYMFIELD_ALPHANUM,
 		SYMFIELD_DEF		// User DEFined
 	};
 	
@@ -577,6 +578,7 @@ public:
 	void set_symbol_list(const uint32_t index, const std::string symbol_list);
 	uint32_t value_dec_u32();
 	uint64_t value_hex_u64();
+	std::string value_string();
 
 	void paint(Painter& painter) override;
 
diff --git a/firmware/portapack-h1-havoc.bin b/firmware/portapack-h1-havoc.bin
index 8b44f7ca9e0784d9c2c29e4654ad4fa26e1ad71f..64300e57abc3683d7878064bc707ebec96a34640 100644
Binary files a/firmware/portapack-h1-havoc.bin and b/firmware/portapack-h1-havoc.bin differ