diff --git a/firmware/application/apps/lge_app.cpp b/firmware/application/apps/lge_app.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ac2b822e6d662e7d618808beb0c2ccc316083db8
--- /dev/null
+++ b/firmware/application/apps/lge_app.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
+ * Copyright (C) 2019 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 "lge_app.hpp"
+
+#include "baseband_api.hpp"
+#include "portapack_persistent_memory.hpp"
+
+#include "crc.hpp"
+#include "string_format.hpp"
+
+#include <cstring>
+#include <stdio.h>
+
+using namespace portapack;
+
+namespace ui {
+
+void LGEView::focus() {
+	tx_view.focus();
+}
+
+LGEView::~LGEView() {
+	transmitter_model.disable();
+	baseband::shutdown();
+}
+
+void LGEView::generate_frame() {
+	CRC<16> crc { 0x1021, 0x90BE };
+	std::vector<uint8_t> frame { };
+	uint8_t payload[9] = { 0x06, 0x0D, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00 };
+	uint8_t out = 0;
+	uint32_t c;
+	
+	payload[6] = field_zone.value();	// Zone
+	
+	// Preamble
+	// Really is 0xAA but the RFM69 skips the very last bit (bug ?)
+	// so the whole preamble should be shifted right 1 bit to simulate that
+	for (c = 0; c < 5; c++)
+		frame.push_back(0x55);
+	
+	frame.push_back(0x2D);	// Sync word
+	frame.push_back(0xD4);
+	
+	crc.process_bytes(payload, 9 - 2);
+	
+	payload[7] = crc.checksum() >> 8;
+	payload[8] = crc.checksum() & 0xFF;
+	
+	// Manchester-encode payload
+	for (c = 0; c < 9; c++) {
+		uint8_t byte = payload[c];
+		for (uint32_t b = 0; b < 8; b++) {
+			out <<= 2;
+			
+			if (byte & 0x80)
+				out |= 0b10;
+			else
+				out |= 0b01;
+			
+			if ((b & 3) == 3)
+				frame.push_back(out);
+			
+			byte <<= 1;
+		}
+	}
+	
+	frame_size = frame.size();
+	
+	/*std::string debug_str { "" };
+	
+	for (c = 0; c < 10; c++)
+		debug_str += (to_string_hex(frame[c], 2) + " ");
+	
+	text_messagea.set(debug_str);
+
+	debug_str = "";
+	for (c = 15; c < frame_size; c++)
+		debug_str += (to_string_hex(frame[c], 2) + " ");
+	
+	text_messageb.set(debug_str);*/
+
+	// Copy for baseband
+	memcpy(shared_memory.bb_data.data, frame.data(), frame_size);
+}
+
+void LGEView::start_tx() {
+	if (tx_mode == ALL) {
+		transmitter_model.set_tuning_frequency(channels[channel_index]);
+		tx_view.on_show();	// Refresh tuning frequency display
+		tx_view.set_dirty();
+	}
+	transmitter_model.set_sampling_rate(2280000);
+	transmitter_model.set_rf_amp(true);
+	transmitter_model.set_baseband_bandwidth(1750000);
+	transmitter_model.enable();
+
+		chThdSleep(100);
+		
+	baseband::set_fsk_data(frame_size * 8, 2280000 / 9600, 4000, 256);
+}
+
+void LGEView::stop_tx() {
+	tx_mode = IDLE;
+	transmitter_model.disable();
+	tx_view.set_transmitting(false);
+}
+
+void LGEView::on_tx_progress(const uint32_t progress, const bool done) {
+	(void)progress;
+	
+	if (!done) return;
+	
+	transmitter_model.disable();
+	
+	if (repeats < 2) {
+		chThdSleep(100);
+		repeats++;
+		start_tx();
+	} else {
+		if (tx_mode == ALL) {
+			if (channel_index < 2) {
+				channel_index++;
+				repeats = 0;
+				start_tx();
+			} else {
+				stop_tx();
+			}
+		} else {
+			stop_tx();
+		}
+	}
+}
+
+LGEView::LGEView(NavigationView& nav) {
+	baseband::run_image(portapack::spi_flash::image_tag_fsktx);
+	
+	add_children({
+		&labels,
+		&field_zone,
+		&checkbox_channels,
+		&text_messagea,
+		&text_messageb,
+		&tx_view
+	});
+	
+	field_zone.set_value(1);
+	checkbox_channels.set_value(true);
+	
+	generate_frame();
+	
+	field_zone.on_change = [this](int32_t) {
+		generate_frame();
+	};
+	
+	tx_view.on_edit_frequency = [this, &nav]() {
+		auto new_view = nav.push<FrequencyKeypadView>(receiver_model.tuning_frequency());
+		new_view->on_changed = [this](rf::Frequency f) {
+			receiver_model.set_tuning_frequency(f);
+		};
+	};
+	
+	tx_view.on_start = [this]() {
+		if (tx_mode == IDLE) {
+			generate_frame();
+			repeats = 0;
+			channel_index = 0;
+			tx_mode = checkbox_channels.value() ? ALL : SINGLE;
+			tx_view.set_transmitting(true);
+			start_tx();
+		}
+	};
+	
+	tx_view.on_stop = [this]() {
+		stop_tx();
+	};
+}
+
+} /* namespace ui */
diff --git a/firmware/application/apps/lge_app.hpp b/firmware/application/apps/lge_app.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0581b9bf81201f570a9f22ead078eb172f35266a
--- /dev/null
+++ b/firmware/application/apps/lge_app.hpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
+ * Copyright (C) 2019 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.hpp"
+#include "ui_widget.hpp"
+#include "ui_navigation.hpp"
+#include "ui_transmitter.hpp"
+#include "ui_font_fixed_8x16.hpp"
+
+#include "message.hpp"
+#include "transmitter_model.hpp"
+#include "portapack.hpp"
+
+namespace ui {
+
+class LGEView : public View {
+public:
+	LGEView(NavigationView& nav);
+	~LGEView();
+	
+	void focus() override;
+	
+	std::string title() const override { return "LGE tool"; };
+
+private:
+	enum tx_modes {
+		IDLE = 0,
+		SINGLE,
+		ALL
+	};
+	
+	tx_modes tx_mode = IDLE;
+	
+	uint32_t frame_size { 0 };
+	uint32_t repeats { 0 };
+	uint32_t channel_index { 0 };
+	
+	rf::Frequency channels[3] = { 868067000, 868183000, 868295000 };
+
+	void start_tx();
+	void stop_tx();
+	void generate_frame();
+	void on_tx_progress(const uint32_t progress, const bool done);
+	
+	Labels labels {
+		{ { 7 * 8, 4 * 8 }, "NO FUN ALLOWED !", Color::red() },
+		{ { 11 * 8, 8 * 8 }, "Zone:", Color::light_grey() }
+	};
+	
+	NumberField field_zone {
+		{ 16 * 8, 8 * 8 },
+		1,
+		{ 1, 6 },
+		16,
+		'0'
+	};
+	
+	Checkbox checkbox_channels {
+		{ 7 * 8, 14 * 8 },
+		12,
+		"All channels"
+	};
+	
+	Text text_messagea {
+		{ 0 * 8, 10 * 16, 30 * 8, 16 },
+		""
+	};
+	Text text_messageb {
+		{ 0 * 8, 11 * 16, 30 * 8, 16 },
+		""
+	};
+	
+	TransmitterView tx_view {
+		16 * 16,
+		10000,
+		12
+	};
+	
+	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, message.done);
+		}
+	};
+};
+
+} /* namespace ui */
diff --git a/firmware/application/apps/ui_siggen.cpp b/firmware/application/apps/ui_siggen.cpp
index 7d55213dbb5df759328879cd889bc031feab0cd2..52206b25fd6004a730a15eb67818ea15d7346ecf 100644
--- a/firmware/application/apps/ui_siggen.cpp
+++ b/firmware/application/apps/ui_siggen.cpp
@@ -42,23 +42,28 @@ SigGenView::~SigGenView() {
 	baseband::shutdown();
 }
 
+void SigGenView::update_config() {
+	baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), field_stop.value());
+}
+
+void SigGenView::update_tone() {
+	baseband::set_siggen_tone(symfield_tone.value_dec_u32());
+}
+
 void SigGenView::start_tx() {
 	transmitter_model.set_sampling_rate(1536000);
 	transmitter_model.set_rf_amp(true);
 	transmitter_model.set_baseband_bandwidth(1750000);
 	transmitter_model.enable();
 	
-	baseband::set_siggen_tone(symfield_tone.value_dec_u32());
+	update_tone();
 	
-	auto duration = field_stop.value();
+	/*auto duration = field_stop.value();
 	if (!checkbox_auto.value())
-		duration = 0;
-	baseband::set_siggen_config(transmitter_model.channel_bandwidth(), options_shape.selected_index_value(), duration);
+		duration = 0;*/
+	update_config();
 }
 
-void SigGenView::update_tone() {
-	baseband::set_siggen_tone(symfield_tone.value_dec_u32());
-}
 
 void SigGenView::on_tx_progress(const uint32_t progress, const bool done) {
 	(void) progress;
@@ -87,6 +92,8 @@ SigGenView::SigGenView(
 	
 	options_shape.on_change = [this](size_t, OptionsField::value_t v) {
 		text_shape.set(shape_strings[v]);
+		if (auto_update)
+			update_config();
 	};
 	options_shape.set_selected_index(0);
 	text_shape.set(shape_strings[0]);
@@ -99,6 +106,7 @@ SigGenView::SigGenView(
 	
 	button_update.on_select = [this](Button&) {
 		update_tone();
+		update_config();
 	};
 	
 	checkbox_auto.on_select = [this](Checkbox&, bool v) {
diff --git a/firmware/application/apps/ui_siggen.hpp b/firmware/application/apps/ui_siggen.hpp
index 20ac4af69230ec773e7108523a19d3a052014532..dde7a39444356fea5682d4c66cc8982d03911fcb 100644
--- a/firmware/application/apps/ui_siggen.hpp
+++ b/firmware/application/apps/ui_siggen.hpp
@@ -44,6 +44,7 @@ public:
 
 private:
 	void start_tx();
+	void update_config();
 	void update_tone();
 	void on_tx_progress(const uint32_t progress, const bool done);
 	
diff --git a/firmware/application/apps/ui_soundboard.cpp b/firmware/application/apps/ui_soundboard.cpp
deleted file mode 100644
index f8ed83cf3a1b123a4d80b2f001538a4bc0c90244..0000000000000000000000000000000000000000
--- a/firmware/application/apps/ui_soundboard.cpp
+++ /dev/null
@@ -1,315 +0,0 @@
-/*
- * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
- * Copyright (C) 2016 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.
- */
-
-// To prepare samples: for f in ./*.wav; do sox "$f" -r 48000 -c 1 -b8 --norm "conv/$f"; done
-
-#include "ui_soundboard.hpp"
-#include "lfsr_random.hpp"
-#include "string_format.hpp"
-#include "tonesets.hpp"
-
-using namespace tonekey;
-using namespace portapack;
-
-namespace ui {
-
-// TODO: Use Sharebrained's PRNG
-void SoundBoardView::do_random() {
-	uint32_t id;
-	
-	chThdSleepMilliseconds(500);
-	
-	lfsr_v = lfsr_iterate(lfsr_v);
-	id = lfsr_v % max_sound;
-
-	play_sound(id);
-	
-	buttons[id % 15].focus();
-	page = id / 15;
-	
-	refresh_buttons(id);
-}
-
-bool SoundBoardView::is_active() const {
-	return (bool)replay_thread;
-}
-
-void SoundBoardView::stop(const bool do_loop) {
-	if( is_active() )
-		replay_thread.reset();
-	
-	if (do_loop && check_loop.value()) {
-		play_sound(playing_id);
-	} else {
-		radio::disable();
-		//button_play.set_bitmap(&bitmap_play);
-	}
-	ready_signal = false;
-}
-
-void SoundBoardView::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);
-}
-
-void SoundBoardView::set_ready() {
-	ready_signal = true;
-}
-
-void SoundBoardView::focus() {
-	buttons[0].focus();
-
-	if (!max_sound)
-		nav_.display_modal("No files", "No files in /WAV/ directory", ABORT, nullptr);
-}
-
-void SoundBoardView::on_tuning_frequency_changed(rf::Frequency f) {
-	transmitter_model.set_tuning_frequency(f);
-}
-
-void SoundBoardView::file_error() {
-	nav_.display_modal("Error", "File read error.");
-}
-
-void SoundBoardView::play_sound(uint16_t id) {
-	uint32_t sample_rate = 0;
-	auto reader = std::make_unique<WAVFileReader>();
-	uint32_t tone_key_index = options_tone_key.selected_index();
-	
-	stop(false);
-
-	if (!reader->open(sounds[id].path)) {
-		file_error();
-		return;
-	}
-	
-	playing_id = id;
-	
-	progressbar.set_max(reader->sample_count());
-	sample_rate = reader->sample_rate() * 32;
-	
-	if( reader ) {
-		//button_play.set_bitmap(&bitmap_stop);
-		baseband::set_sample_rate(sample_rate);
-		
-		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);
-			}
-		);
-	}
-	
-	baseband::set_audiotx_config(
-		0,	// Divider is unused
-		number_bw.value() * 1000,
-		0,	// Gain is unused
-		TONES_F2D(tone_key_frequency(tone_key_index), sample_rate)
-	);
-	
-	radio::enable({
-		receiver_model.tuning_frequency(),
-		sample_rate,
-		1750000,
-		rf::Direction::Transmit,
-		receiver_model.rf_amp(),
-		static_cast<int8_t>(receiver_model.lna()),
-		static_cast<int8_t>(receiver_model.vga())
-	});
-}
-
-void SoundBoardView::show_infos(uint16_t id) {
-	text_duration.set(to_string_time_ms(sounds[id].ms_duration));
-	
-	text_title.set(sounds[id].title);
-}
-
-void SoundBoardView::refresh_buttons(uint16_t id) {
-	size_t n = 0, n_sound;
-	
-	text_page.set("Page " + to_string_dec_uint(page + 1) + "/" + to_string_dec_uint(max_page));
-	
-	for (auto& button : buttons) {
-		n_sound = (page * 15) + n;
-		
-		button.id = n_sound;
-		
-		if (n_sound < max_sound) {
-			button.set_text(sounds[n_sound].path.stem().string().substr(0, 8));
-			button.set_style(styles[sounds[n_sound].path.stem().string()[0] & 3]);
-		} else {
-			button.set_text("- - -");
-			button.set_style(styles[0]);
-		}
-		
-		n++;
-	}
-	
-	show_infos(id);
-}
-
-bool SoundBoardView::change_page(Button& button, const KeyEvent key) {
-	// Stupid way to find out if the button is on the sides
-	if (button.screen_pos().x() < 32) {
-		if ((key == KeyEvent::Left) && (page > 0)) {
-			page--;
-			refresh_buttons(button.id);
-			return true;
-		}
-	} else if (button.screen_pos().x() > 120) {
-		if ((key == KeyEvent::Right) && (page < max_page - 1)) {
-			page++;
-			refresh_buttons(button.id);
-			return true;
-		}
-	}
-	return false;
-}
-
-void SoundBoardView::on_tx_progress(const uint32_t progress) {
-	progressbar.set_value(progress);
-}
-
-SoundBoardView::SoundBoardView(
-	NavigationView& nav
-) : nav_ (nav)
-{
-	auto reader = std::make_unique<WAVFileReader>();
-	uint8_t c = 0;
-	
-	for(const auto& entry : std::filesystem::directory_iterator(u"WAV", u"*.WAV")) {
-		if( std::filesystem::is_regular_file(entry.status()) ) {
-			if (reader->open(u"WAV/" + entry.path().native())) {
-				if ((reader->channels() == 1) && (reader->bits_per_sample() == 8)) {
-					sounds[c].ms_duration = reader->ms_duration();
-					sounds[c].path = u"WAV/" + entry.path().native();
-					std::string title = reader->title().substr(0, 20);
-					if (title != "")
-						sounds[c].title = title;
-					else
-						sounds[c].title = "-";
-					c++;
-					if (c == 60) break;		// Limit to 60 files (4 pages)
-				}
-			}
-		}
-	}
-	
-	baseband::run_image(portapack::spi_flash::image_tag_audio_tx);
-	
-	max_sound = c;
-	max_page = (max_sound + 15 - 1) / 15;	// 3 * 5 = 15 buttons per page
-	
-	add_children({
-		&labels,
-		&field_frequency,
-		&number_bw,
-		&options_tone_key,
-		&text_title,
-		&text_page,
-		&text_duration,
-		&progressbar,
-		&check_loop,
-		&button_random,
-		&button_exit
-	});
-	
-	tone_keys_populate(options_tone_key);
-	options_tone_key.set_selected_index(0);
-	
-	const auto button_fn = [this](Button& button) {
-		tx_mode = NORMAL;
-		this->play_sound(button.id);
-	};
-	
-	const auto button_focus = [this](Button& button) {
-		this->show_infos(button.id);
-	};
-	
-	const auto button_dir = [this](Button& button, const KeyEvent key) {
-		return change_page(button, key);
-	};
-	
-	// Generate buttons
-	size_t n = 0;
-	for(auto& button : buttons) {
-		add_child(&button);
-		button.on_select = button_fn;
-		button.on_highlight = button_focus;
-		button.on_dir = button_dir;
-		button.set_parent_rect({
-			static_cast<Coord>((n % 3) * 78 + 3),
-			static_cast<Coord>((n / 3) * 38 + 24),
-			78, 38
-		});
-		n++;
-	}
-	refresh_buttons(0);
-	
-	check_loop.set_value(false);
-	number_bw.set_value(12);
-
-	field_frequency.set_value(transmitter_model.tuning_frequency());
-	field_frequency.set_step(10000);
-	field_frequency.on_change = [this](rf::Frequency f) {
-		this->on_tuning_frequency_changed(f);
-	};
-	field_frequency.on_edit = [this, &nav]() {
-		// TODO: Provide separate modal method/scheme?
-		auto new_view = nav.push<FrequencyKeypadView>(transmitter_model.tuning_frequency());
-		new_view->on_changed = [this](rf::Frequency f) {
-			this->on_tuning_frequency_changed(f);
-			this->field_frequency.set_value(f);
-		};
-	};
-	
-	button_random.on_select = [this](Button&){
-		if (tx_mode == NORMAL) {
-			tx_mode = RANDOM;
-			button_random.set_text("STOP");
-			do_random();
-		} else {
-			tx_mode = NORMAL;
-			button_random.set_text("Random");
-		}
-	};
-
-	button_exit.on_select = [&nav](Button&){
-		nav.pop();
-	};
-}
-
-SoundBoardView::~SoundBoardView() {
-	transmitter_model.disable();
-	baseband::shutdown();
-}
-
-}
diff --git a/firmware/application/apps/ui_soundboard.hpp b/firmware/application/apps/ui_soundboard.hpp
deleted file mode 100644
index 5f9e65b075cc12a81d6478826d8695365b6cc2cf..0000000000000000000000000000000000000000
--- a/firmware/application/apps/ui_soundboard.hpp
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
- * Copyright (C) 2016 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 __UI_SOUNDBOARD_H__
-#define __UI_SOUNDBOARD_H__
-
-#include "ui.hpp"
-#include "ui_widget.hpp"
-#include "ui_font_fixed_8x16.hpp"
-#include "replay_thread.hpp"
-#include "baseband_api.hpp"
-#include "ui_receiver.hpp"
-#include "io_wave.hpp"
-#include "tone_key.hpp"
-
-namespace ui {
-
-class SoundBoardView : public View {
-public:
-	SoundBoardView(NavigationView& nav);
-	~SoundBoardView();
-
-	SoundBoardView(const SoundBoardView&) = delete;
-	SoundBoardView(SoundBoardView&&) = delete;
-	SoundBoardView& operator=(const SoundBoardView&) = delete;
-	SoundBoardView& operator=(SoundBoardView&&) = delete;
-
-	void focus() override;
-	
-	std::string title() const override { return "Soundboard"; };
-	
-private:
-	NavigationView& nav_;
-	
-	enum tx_modes {
-		NORMAL = 0,
-		RANDOM
-	};
-	
-	tx_modes tx_mode = NORMAL;
-	
-	struct sound {
-		std::filesystem::path path { };
-		uint32_t ms_duration = 0;
-		std::string title { };
-	};
-	
-	uint32_t sample_counter { 0 };
-	uint32_t sample_duration { 0 };
-	uint8_t page = 0;
-	
-	uint32_t lfsr_v = 0x13377331;
-	
-	sound sounds[60];			// 5 pages * 12 buttons
-	uint32_t max_sound { };
-	uint8_t max_page { };
-	uint32_t playing_id { };
-
-	const size_t read_size { 2048 };	// Less ?
-	const size_t buffer_count { 3 };
-	std::unique_ptr<ReplayThread> replay_thread { };
-	bool ready_signal { false };
-	
-	Style style_a {
-		.font = font::fixed_8x16,
-		.background = Color::black(),
-		.foreground = { 255, 51, 153 }
-	};
-	Style style_b {
-		.font = font::fixed_8x16,
-		.background = Color::black(),
-		.foreground = { 153, 204, 0 }
-	};
-	Style style_c {
-		.font = font::fixed_8x16,
-		.background = Color::black(),
-		.foreground = { 51, 204, 204 }
-	};
-	Style style_d {
-		.font = font::fixed_8x16,
-		.background = Color::black(),
-		.foreground = { 153, 102, 255 }
-	};
-
-	std::array<Button, 15> buttons { };
-	const Style * styles[4] = { &style_a, &style_b, &style_c, &style_d };
-	
-	void on_tuning_frequency_changed(rf::Frequency f);
-	
-	void do_random();
-	void show_infos(uint16_t id);
-	bool change_page(Button& button, const KeyEvent key);
-	void refresh_buttons(uint16_t id);
-	void play_sound(uint16_t id);
-	void on_ctcss_changed(uint32_t v);
-	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();
-	void on_tx_progress(const uint32_t progress);
-	
-	Labels labels {
-		{ { 10 * 8, 4 }, "BW:   kHz", Color::light_grey() }
-	};
-	
-	FrequencyField field_frequency {
-		{ 0, 4 },
-	};
-	
-	NumberField number_bw {
-		{ 13 * 8, 4 },
-		3,
-		{ 1, 150 },
-		1,
-		' '
-	};
-	
-	OptionsField options_tone_key {
-		{ 21 * 8, 4 },
-		8,
-		{ }
-	};
-	
-	Text text_title {
-		{ 1 * 8, 28 * 8, 20 * 8, 16 },
-		"-"
-	};
-	
-	Text text_page {
-		{ 22 * 8 - 4, 28 * 8, 8 * 8, 16 },
-		"Page -/-"
-	};
-	
-	Text text_duration {
-		{ 1 * 8, 31 * 8, 5 * 8, 16 }
-	};
-	
-	ProgressBar progressbar {
-		{ 9 * 8, 31 * 8, 20 * 8, 16 }
-	};
-	
-	Checkbox check_loop {
-		{ 8, 274 },
-		4,
-		"Loop"
-	};
-	
-	Button button_random {
-		{ 10 * 8, 34 * 8, 9 * 8, 32 },
-		"Random"
-	};
-	
-	Button button_exit {
-		{ 21 * 8, 34 * 8, 8 * 8, 32 },
-		"Exit"
-	};
-	
-	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/*__UI_SOUNDBOARD_H__*/
diff --git a/firmware/application/bitmap.hpp b/firmware/application/bitmap.hpp
index 0c05f0ccced39036f51c002bd064938f7eb58060..a4950deba3179a5c8784841fb618d6e5f5051e6a 100644
--- a/firmware/application/bitmap.hpp
+++ b/firmware/application/bitmap.hpp
@@ -471,6 +471,28 @@ static constexpr Bitmap bitmap_icon_nuoptix {
 	{ 16, 16 }, bitmap_icon_nuoptix_data
 };
 
+static constexpr uint8_t bitmap_icon_lge_data[] = {
+	0x00, 0x00, 
+	0x80, 0x00, 
+	0xA4, 0x12, 
+	0xA8, 0x0A, 
+	0xD0, 0x05, 
+	0xEC, 0x1B, 
+	0xF0, 0x07, 
+	0xFE, 0xFF, 
+	0xF0, 0x07, 
+	0xEC, 0x1B, 
+	0xD0, 0x05, 
+	0xA8, 0x0A, 
+	0xA4, 0x12, 
+	0x80, 0x00, 
+	0x00, 0x00, 
+	0x00, 0x00, 
+};
+static constexpr Bitmap bitmap_icon_lge {
+	{ 16, 16 }, bitmap_icon_lge_data
+};
+
 static constexpr uint8_t bitmap_icon_file_iq_data[] = {
 	0x98, 0x00, 
 	0x24, 0x06, 
diff --git a/firmware/application/main.cpp b/firmware/application/main.cpp
index 9d460da9df4320bbfbd20b48636a1691788bdad6..1e5703e1fb9a2c2c6b04e7badf12de5d78c120ea 100755
--- a/firmware/application/main.cpp
+++ b/firmware/application/main.cpp
@@ -46,6 +46,8 @@
 //GLITCH: Start of tx using ReplayThread plays a small bit of previous transmission (content of 1 buffer ?)
 //	See fifo.reset_in() ?
 
+//FIXED: Update button in signal gen doesn't work for shape change
+//BUG: Signal gen noise shape doesn't work
 //TODO: Continue acars receiver. See matched filter, probably doesn't shift the spectrum correctly
 //TODO: Add larger description text field in frequency load, under menuview
 //TODO: Allow apps to select a preferred FREQMAN file
diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp
index 997c0e4df0abe84148d1a349bfddd55c2581a0fa..504ce9d6999ae019d9bc8ec570051ff24a6026a0 100644
--- a/firmware/application/ui_navigation.cpp
+++ b/firmware/application/ui_navigation.cpp
@@ -70,6 +70,7 @@
 #include "analog_audio_app.hpp"
 #include "capture_app.hpp"
 #include "ert_app.hpp"
+#include "lge_app.hpp"
 #include "pocsag_app.hpp"
 #include "replay_app.hpp"
 #include "soundboard_app.hpp"
@@ -371,6 +372,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
 		{ "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>(); } },
+		{ "LGE tool", 				ui::Color::yellow(),	&bitmap_icon_lge,		[&nav](){ nav.push<LGEView>(); } },
 		{ "Microphone", 			ui::Color::green(),		&bitmap_icon_microphone,	[&nav](){ nav.push<MicTXView>(); } },
 		{ "Morse code", 			ui::Color::green(),		&bitmap_icon_morse,		[&nav](){ nav.push<MorseView>(); } },
 		{ "Burger pagers", 			ui::Color::yellow(), 	&bitmap_icon_burger,	[&nav](){ nav.push<CoasterPagerView>(); } },
diff --git a/firmware/baseband/proc_fsk.cpp b/firmware/baseband/proc_fsk.cpp
index f5f5658873cc2e75fd43aa694c59f810bdbae879..1b4f9f5f0a596541d72b0bfc2b17509b2fcbb760 100644
--- a/firmware/baseband/proc_fsk.cpp
+++ b/firmware/baseband/proc_fsk.cpp
@@ -32,35 +32,33 @@ void FSKProcessor::execute(const buffer_c8_t& buffer) {
 	
 	// This is called at 2.28M/2048 = 1113Hz
 	
-	if (!configured) return;
-	
 	for (size_t i = 0; i < buffer.count; i++) {
 
-		if (sample_count >= samples_per_bit) {
-			if (bit_pos > length) {
-				// End of data
-				cur_bit = 0;
-				txprogress_message.done = true;
-				shared_memory.application_queue.push(txprogress_message);
-				configured = false;
-			} else {
-				cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80;
-				bit_pos++;
-				if (progress_count >= progress_notice) {
-					progress_count = 0;
-					txprogress_message.progress++;
-					txprogress_message.done = false;
+		if (configured) {
+			if (sample_count >= samples_per_bit) {
+				if (bit_pos > length) {
+					// End of data
+					cur_bit = 0;
+					txprogress_message.done = true;
 					shared_memory.application_queue.push(txprogress_message);
+					configured = false;
 				} else {
-					progress_count++;
+					cur_bit = (shared_memory.bb_data.data[bit_pos >> 3] << (bit_pos & 7)) & 0x80;
+					bit_pos++;
+					if (progress_count >= progress_notice) {
+						progress_count = 0;
+						txprogress_message.progress++;
+						txprogress_message.done = false;
+						shared_memory.application_queue.push(txprogress_message);
+					} else {
+						progress_count++;
+					}
 				}
+				sample_count = 0;
+			} else {
+				sample_count++;
 			}
-			sample_count = 0;
-		} else {
-			sample_count++;
-		}
 		
-		if (configured) {
 			if (cur_bit)
 				phase += shift_one;
 			else
@@ -84,10 +82,10 @@ void FSKProcessor::on_message(const Message* const p) {
 	
 	if (message.id == Message::ID::FSKConfigure) {
 		samples_per_bit = message.samples_per_bit;
-		length = message.stream_length + 4;			// Why 4 ?!
+		length = message.stream_length + 32;			// Why ?!
 		
-		shift_zero = message.shift * (0xFFFFFFFFULL / 2280000);
-		shift_one = -shift_zero;
+		shift_one = message.shift * (0xFFFFFFFFULL / 2280000);
+		shift_zero = -shift_one;
 		
 		progress_notice = message.progress_notice;
 		
diff --git a/firmware/common/portapack_shared_memory.hpp b/firmware/common/portapack_shared_memory.hpp
index b00b450739bbb62f33d682d6ddec3cec0532e071..3235cef155dbf6fbe7fef8d7d86c033bf66efcb9 100644
--- a/firmware/common/portapack_shared_memory.hpp
+++ b/firmware/common/portapack_shared_memory.hpp
@@ -63,7 +63,7 @@ struct SharedMemory {
 		ToneData tones_data;
 		JammerChannel jammer_channels[24];
 		uint8_t data[512];
-	} bb_data { { { { 0, 0 } }, 0, { 0 } } };	// { } ?
+	} bb_data { { { { 0, 0 } }, 0, { 0 } } };
 };
 
 extern SharedMemory& shared_memory;
diff --git a/firmware/graphics/icon_lge.png b/firmware/graphics/icon_lge.png
new file mode 100644
index 0000000000000000000000000000000000000000..3aa05c00088de85e7305aea14f847cf541c58d20
Binary files /dev/null and b/firmware/graphics/icon_lge.png differ
diff --git a/hardware/portapack_h1/case/pp_h1_case_2.scad b/hardware/portapack_h1/case/pp_h1_case_2.scad
deleted file mode 100644
index 8388001908aec5e345594414e6c4c506681e5173..0000000000000000000000000000000000000000
--- a/hardware/portapack_h1/case/pp_h1_case_2.scad
+++ /dev/null
@@ -1,80 +0,0 @@
-use <pp_h1_shell.scad>
-//use <pp_h1_holes.scad>
-use <pp_h1_stack.scad>
-include <pp_h1_parameters.scad>
-
-module case() {
-    difference() {
-        if( case_radiused ) {
-            case_outer_volume_radiused();
-        } else {
-            case_outer_volume_squared();
-        }
-        
-        union() {
-            if( case_radiused ) {
-                case_bottom_void_tool_volume_ball();
-            } else {
-                case_bottom_void_tool_volume_end();
-            }
-            
-            case_pcb_plane_void_tool_volume();
-            pcb_attach_drills_volume();
-            case_bumpers_emboss();
-            translate([0, 0, h1_pcb_thickness]) portapack_h1_stack_drills();
-        }
-    }
-}
-
-/* Cross section */
-module case_cross_section() {
-    difference() {
-        case();
-        translate([70, -10, -10]) cube([100, 100, 100]);
-    }
-}
-
-module case_and_h1() {
-    case();
-    translate([0, 0, h1_pcb_thickness]) hackrf_one();
-}
-
-module case_and_stack() {
-    case();
-    translate([0, 0, h1_pcb_thickness]) portapack_h1_stack();
-}
-
-module stack_case_interference() {
-    intersection() {
-        case();
-        translate([0, 0, h1_pcb_thickness]) portapack_h1_stack();
-    }
-}
-
-module stack_stack_interference() {
-    // Ensure stack and spacers do not interfere.
-    intersection() {
-        union() {
-            portapack_h1_stack_hackrf_one();
-            portapack_h1_stack_portapack();
-        }
-        portapack_h1_stack_spacers();
-    }
-    
-    // Ensure screws do not interfere with stack.
-    intersection() {
-        union() {
-            portapack_h1_stack_hackrf_one();
-            portapack_h1_stack_spacers();
-            portapack_h1_stack_portapack();
-        }
-        portapack_h1_stack_screws();
-    }
-}
-
-case();
-//case_and_h1();
-//case_and_stack();
-//case_cross_section();
-//stack_case_interference();
-//stack_stack_interference();
diff --git a/hardware/portapack_h1/case/pp_h1_parameters.scad b/hardware/portapack_h1/case/pp_h1_parameters.scad
deleted file mode 100644
index 113f7cb70bd4308b8b823c75d8360b4ce5310432..0000000000000000000000000000000000000000
--- a/hardware/portapack_h1/case/pp_h1_parameters.scad
+++ /dev/null
@@ -1,79 +0,0 @@
-pcb_l = 120;
-pcb_w = 75;
-pcb_corner_r = 4;
-pcb_hole_r = 3.2 / 2;
-pcb_hole_pad_r = 5.6 / 2;
-
-h1_pcb_thickness = 1.64;
-pp_h1_pcb_thickness = 1.56;
-
-spacer_height = 12.0;
-
-bolt_drill_d = 3.0;
-pcb_attach_drills_depth = 4.0;
-
-pcb_case_clearance = 0.5;
-case_thickness = 1.5;
-case_bottom_thickness = case_thickness * 2;
-h1_pcb_bottom_clearance = 4.0;
-case_bottom_tool_r = 3.0;
-
-h1_led_hole_diameter = 2;
-h1_led_diffuser_thickness = 0.85;
-
-case_pcb_n_clearance = h1_led_diffuser_thickness + 0.15;
-case_pcb_w_clearance = pcb_case_clearance;
-case_pcb_e_clearance = pcb_case_clearance;
-case_pcb_s_clearance = pcb_case_clearance;
-
-lcd_thickness = 3.8;
-case_lid_thickness = 3.0 / 16.0 * 25.4;
-
-case_height_above_datum = h1_pcb_thickness + spacer_height + pp_h1_pcb_thickness + case_lid_thickness;
-case_height_below_datum = case_bottom_thickness + h1_pcb_bottom_clearance;
-case_height = case_height_below_datum + case_height_above_datum;
-
-attach_foot_r = pcb_hole_pad_r;
-attach_drill_r = bolt_drill_d / 2.0;
-
-case_bumper_d = 0.5 * 25.4;
-case_bumper_clearance = 0.0;
-case_bumper_emboss_depth = 1.0;
-
-case_radiused = true;
-
-case_bumper_inset_from_pcb_edge = case_radiused ? 14.0 : 12.0;
-
-mounting_drills = [
-    [4, 4],
-    [116, 4],
-    [4, pcb_w - 4],
-    [116, pcb_w - 4]
-];
-
-module pcb_extents() {
-    square([pcb_l, pcb_w]);
-}
-
-module pcb_outline() {
-    minkowski() {
-        offset(r=-pcb_corner_r) {
-            pcb_extents();
-        }
-        circle(r=pcb_corner_r);
-    }
-}
-
-module pcb_outline_clearance() {
-    minkowski() {
-        offset(r=-pcb_corner_r) {
-            polygon([
-                [0 - case_pcb_n_clearance, 0 - case_pcb_w_clearance],
-                [0 - case_pcb_n_clearance, pcb_w + case_pcb_e_clearance],
-                [pcb_l + case_pcb_s_clearance, pcb_w + case_pcb_e_clearance],
-                [pcb_l + case_pcb_s_clearance, 0 - case_pcb_w_clearance]
-            ]);
-        }
-        circle(r=pcb_corner_r);
-    }
-}
diff --git a/hardware/portapack_h1/case/pp_h1_shell.scad b/hardware/portapack_h1/case/pp_h1_shell.scad
deleted file mode 100644
index 2b93f437661e9c7c34abc665f369791d1501c329..0000000000000000000000000000000000000000
--- a/hardware/portapack_h1/case/pp_h1_shell.scad
+++ /dev/null
@@ -1,150 +0,0 @@
-include <pp_h1_parameters.scad>
-
-$fs=0.1;
-
-module attach_corner() {
-    circle(attach_foot_r);
-    polygon([[-10, -10],[attach_foot_r, -10],[attach_foot_r, 0],[0, attach_foot_r],[-10, attach_foot_r]]);
-}
-
-module attach_side() {
-    circle(attach_foot_r);
-    translate([0, -attach_foot_r]) square([10, attach_foot_r * 2]);
-}
-
-module attach_center() {
-    circle(attach_foot_r);
-}
-
-module pcb_supports() {
-    translate(mounting_drills[0]) attach_corner();
-    translate(mounting_drills[1]) rotate(90) attach_corner();
-    translate(mounting_drills[2]) rotate(270) attach_corner();
-    translate(mounting_drills[3]) rotate(180) attach_corner();
-}
-
-module pcb_attach_drill_outline() {
-    circle(r=attach_drill_r);
-}
-
-module pcb_attach_drills_outline() {
-    for(p = mounting_drills) {
-        translate(p) pcb_attach_drill_outline();
-    }
-}
-
-module pcb_attach_drills_volume() {
-    translate([0, 0, -pcb_attach_drills_depth]) linear_extrude(height=30) {
-        pcb_attach_drills_outline();
-    }
-}
-
-module case_bottom_void_edge() {
-    // Edge of PCB, plus case clearance, minus board supports.
-    difference() {
-        pcb_outline_clearance();
-        pcb_supports();
-    }
-}
-
-module case_bottom_void_tool_path() {
-    // Tool path to cut bottom of case.
-    offset(r=-case_bottom_tool_r) {
-        case_bottom_void_edge();
-    }
-}
-
-module case_bottom_void_tool_volume_ball() {
-    $fs=2;
-    $fn=18;
-    // Tool cut volume for bottom of case.
-    // Z=0 at bottom plane of H1 PCB
-    translate([0, 0, -h1_pcb_bottom_clearance + case_bottom_tool_r]) minkowski() {
-        linear_extrude(height=50, convexity=10) {
-            case_bottom_void_tool_path();
-        }
-        sphere(r=case_bottom_tool_r);
-    }
-}
-
-module case_outer_volume_radiused() {
-    $fs=2;
-    $fn=18;
-    tool_r = case_bottom_tool_r + case_thickness;
-    tz = h1_pcb_bottom_clearance + case_bottom_thickness - tool_r;
-    difference() {
-        // Rounded volume
-        translate([0, 0, -tz]) {
-            minkowski() {
-                linear_extrude(height=30, convexity=10) {
-                    offset(r=-case_bottom_tool_r) {
-                        pcb_outline_clearance();
-                    }
-                }
-                sphere(r=tool_r);
-            }
-        }
-        
-        // Cut off the top.
-        translate([-10, -10, case_height_above_datum]) cube([200, 200, 200]);
-    }
-}
-
-module case_bottom_void_tool_volume_end() {
-    // Tool cut volume for bottom of case.
-    // Z=0 at bottom plane of H1 PCB
-    translate([0, 0, -h1_pcb_bottom_clearance]) {
-        linear_extrude(height=50) {
-            minkowski() {
-                case_bottom_void_tool_path();
-                circle(r=case_bottom_tool_r);
-            }
-        }
-    }
-}
-
-module case_bumper_emboss_outline() {
-    circle(r=case_bumper_d / 2 + case_bumper_clearance);
-}
-
-module case_bumper_emboss_tool() {
-    height = 10;
-    translate([0, 0, -height]) linear_extrude(height=height) {
-        case_bumper_emboss_outline();
-    }
-}
-
-module case_bumpers_emboss() {
-    tz = case_height_below_datum - case_bumper_emboss_depth;
-    translate([0, 0, -tz]) {
-        translate([case_bumper_inset_from_pcb_edge, case_bumper_inset_from_pcb_edge, 0]) case_bumper_emboss_tool();
-        translate([pcb_l - case_bumper_inset_from_pcb_edge, case_bumper_inset_from_pcb_edge, 0]) case_bumper_emboss_tool();
-        translate([case_bumper_inset_from_pcb_edge, pcb_w - case_bumper_inset_from_pcb_edge, 0]) case_bumper_emboss_tool();
-        translate([pcb_l - case_bumper_inset_from_pcb_edge, pcb_w - case_bumper_inset_from_pcb_edge, 0]) case_bumper_emboss_tool();
-    }
-}
-
-module case_pcb_plane_void_tool_edge() {
-    offset(r=-pcb_corner_r) {
-        pcb_outline_clearance();
-    }
-}
-
-module case_pcb_plane_void_tool_volume() {
-    linear_extrude(height=30, convexity=10) {
-        minkowski() {
-            case_pcb_plane_void_tool_edge();
-            circle(r=pcb_corner_r);
-        }
-    }
-}
-
-module case_outer_volume_squared() {
-    t = case_bottom_thickness + h1_pcb_bottom_clearance;
-    translate([0, 0, -t]) linear_extrude(height=t + case_height_above_datum) {
-        minkowski() {
-            pcb_outline();
-            circle(r=pcb_case_clearance + case_thickness);
-        }
-    }
-}
diff --git a/hardware/portapack_h1/case/pp_h1_stack.scad b/hardware/portapack_h1/case/pp_h1_stack.scad
deleted file mode 100644
index 341831ae5d985a7c6b519f8fbe92109af5ceed9f..0000000000000000000000000000000000000000
--- a/hardware/portapack_h1/case/pp_h1_stack.scad
+++ /dev/null
@@ -1,841 +0,0 @@
-include <pp_h1_parameters.scad>
-
-$fs=0.1;
-
-module pcb_mounting_hole_drill() {
-    circle(r=pcb_hole_r);
-}
-
-module pcb_mounting_hole_drills() {
-    translate([ 64, 104]) pcb_mounting_hole_drill();
-    translate([126, 104]) pcb_mounting_hole_drill();
-    translate([176, 104]) pcb_mounting_hole_drill();
-    translate([ 64, 171]) pcb_mounting_hole_drill();
-    translate([131, 144]) pcb_mounting_hole_drill();
-    translate([176, 171]) pcb_mounting_hole_drill();
-}
-
-////////////////////////////////////////////////////////
-
-module pcb_cutout_antenna_pos() {
-    x = 61.5;
-    y1 = 154.75;
-    y2 = 167.25;
-    r = 1.5;
-    minkowski() {
-        polygon(
-            points=[[x+8,y1-4],[x,y1-4],[x,y1],[x+4,y1],[x+4,y2],[x,y2],[x,y2+4],[x+8,y2+4]]
-        );
-        circle(r=r);
-    };
-}
-
-module pcb_cutout_antenna_neg_curve() {
-    x = 59.5;
-    y1 = 157;
-    y2 = 165;
-    r = 1.5;
-    minkowski() {
-        polygon(
-            points=[[x-2,y1],[x,y1],[x,y2],[x-2,y2]]
-        );
-        circle(r=r);
-    };
-}
-
-module pcb_cutout_antenna_neg() {
-    x = 60.5;
-    y1 = 157;
-    y2 = 165;
-    w = -4;
-    union() {
-        polygon(
-            points=[[x+w,y1-3],[x,y1-3],[x,y2+3],[x+w,y2+3]]
-        );
-        pcb_cutout_antenna_neg_curve();
-    }
-}
-
-module pcb_cutout_antenna() {
-    difference() {
-        pcb_cutout_antenna_neg();
-        pcb_cutout_antenna_pos();
-    }
-}
-
-////////////////////////////////////////////////////////
-
-module pcb_cutout_clocks_pos() {
-    x = 178.5;
-    y1 = 138.75;
-    y2 = 169.25;
-    r = 1.5;
-    w = 8;
-    w2 = 4;
-    minkowski() {
-        polygon(
-            points=[[x-w,y1-4],[x,y1-4],[x,y1],[x-w2,y1],[x-w2,y2],[x,y2],[x,y2+4],[x-w,y2+4]]
-        );
-        circle(r=r);
-    };
-}
-
-module pcb_cutout_clock_neg_curve() {
-    x = 180.5;
-    y1 = 141;
-    y2 = 167;
-    r = 1.5;
-    w = 2;
-    minkowski() {
-        polygon(
-            points=[[x,y1],[x+w,y1],[x+w,y2],[x,y2]]
-        );
-        circle(r=r);
-    };
-}
-
-module pcb_cutout_clocks_neg() {
-    x = 179.5;
-    y1 = 141;
-    y2 = 167;
-    w = 6;
-    union() {
-        polygon(
-            points=[[x,y1-3],[x+w,y1-3],[x+w,y2+3],[x,y2+3]]
-        );
-        pcb_cutout_clock_neg_curve();
-    }
-}
-
-module pcb_cutout_clocks() {
-    difference() {
-        pcb_cutout_clocks_neg();
-        pcb_cutout_clocks_pos();
-    }
-}
-
-////////////////////////////////////////////////////////
-
-bulkhead_w = 6.35;
-bulkhead_h = 6.35;
-bulkhead_thickness = 1.02;
-
-barrel_d = 6.2;
-barrel_clip_d = 5.5;
-barrel_l = 12.45 - bulkhead_thickness;
-
-barrel_r = barrel_d / 2;
-barrel_clip_r = barrel_clip_d / 2;
-
-peg_l = 3.81;
-peg_w = 1.02;
-peg_bottom_h = 0.76;
-peg_top_h = 1.27;
-peg_space = 1.78;
-
-module sma_73251_2120_pegs() {
-    peg_top_ty = bulkhead_h/2 - peg_bottom_h - peg_space - peg_top_h;
-    linear_extrude(height=peg_l)
-    {
-    translate([-bulkhead_w/2, peg_top_ty])
-        square([peg_w, peg_top_h]);
-    translate([bulkhead_w/2 - peg_w, peg_top_ty])
-        square([peg_w, peg_top_h]);
-    translate([-bulkhead_w/2, bulkhead_h/2 - peg_bottom_h])
-        square([peg_w, peg_bottom_h]);
-    translate([bulkhead_w/2 - peg_w, bulkhead_h/2 - peg_bottom_h])
-        square([peg_w, peg_bottom_h]);
-    }
-}
-
-module sma_73251_2120_barrel_outline_circle() {
-    circle(r=barrel_r);
-}
-
-module sma_73251_2120_barrel_outline() {
-    intersection() {
-        sma_73251_2120_barrel_outline_circle();
-        square([barrel_clip_d, barrel_d + 1], center=true);
-    }
-}
-
-module sma_73251_2120_barrel() {
-    linear_extrude(height=barrel_l) {
-        sma_73251_2120_barrel_outline();
-    }
-}
-
-module sma_73251_2120_bulkhead() {
-    linear_extrude(height=bulkhead_thickness) {
-        square([bulkhead_w, bulkhead_h], center=true);
-    }
-}
-
-module sma_73251_2120_union() {
-    union() {
-        translate([0, 0, -peg_l]) sma_73251_2120_pegs();
-        sma_73251_2120_bulkhead();
-        translate([0, 0, bulkhead_thickness]) sma_73251_2120_barrel();
-    }
-}
-/*
-module sma_73251_2120() {
-    ty = bulkhead_h/2 - peg_bottom_h - peg_space/2;
-    rotate([90, 0, 0]) translate([0, -ty, 0]) {
-        union() {
-            translate([0, 0, -peg_l]) sma_73251_2120_pegs();
-            sma_73251_2120_bulkhead();
-            translate([0, 0, bulkhead_thickness]) sma_73251_2120_barrel();
-        }
-    }
-}
-*/
-module sma_73251_2120_orient(board_thickness) {
-    // Align so that top surface of bottom peg is at z=0 (bottom of PCB).
-    t = peg_top_h / 2 + (peg_space - board_thickness) / 2;
-    translate([0, 0, -t]) rotate([90, 0, -90]) {
-        children();
-    }
-}
-
-module sma_73251_2120(refdes, board_thickness) {
-    sma_73251_2120_orient(board_thickness) {
-        sma_73251_2120_union();
-    }
-}
-
-module sma_73251_2120_drill(tolerance, board_thickness) {
-    sma_73251_2120_orient(board_thickness) {
-        linear_extrude(height=30) {
-            minkowski() {
-                sma_73251_2120_barrel_outline_circle();
-                circle(r=tolerance);
-            }
-        }
-    }
-}
-
-////////////////////////////////////////////////////////
-
-module led(refdes, c) {
-    rotate(90) translate([-0.25, -2.15/2, -0.60]) {
-        color("gray") linear_extrude(height=0.60) {
-            square([0.50, 2.15]);
-            translate([0, 2.15/2]) circle(r=0.5);
-        }
-    }
-}
-
-module led_drill() {
-    translate([0, -0.25, -0.3]) {
-        rotate([90, 0, 0]) {
-            cylinder(d=h1_led_hole_diameter, h=10);
-        }
-    }
-}
-
-////////////////////////////////////////////////////////
-
-module sw_outline() {
-    circle(d=3.51, center=true);
-}
-
-sw_a = 3.25;
-sw_l = 5.85;
-sw_tz = sw_l - sw_a;
-
-sw_large_hole_spacing = 7.01;
-sw_large_hole_diameter = 1.30;
-sw_small_hole_spacing = 4.50;
-sw_small_hole_diameter = 0.99;
-sw_large_small_hole_spacing = 2.49;
-sw_pin_length_below_datum = 3.51;
-
-sw_button_z_offset = 4.01;
-
-module sw() {
-    rotate([180, 0, 90]) {
-        rotate([90, 0, 0]) {
-            translate([0, 4.01, sw_tz]) {
-                color("gray") translate([-7.11/2, -sw_button_z_offset, -3.68]) linear_extrude(height=3.68) square([7.11, 7.01]);
-                color("blue") linear_extrude(height=sw_a) sw_outline();
-            }
-        }
-        
-        rotate([180, 0, 180]) linear_extrude(height=sw_pin_length_below_datum) {
-            translate([-sw_large_hole_spacing/2, sw_large_small_hole_spacing]) circle(d=sw_large_hole_diameter);
-            translate([ sw_large_hole_spacing/2, sw_large_small_hole_spacing]) circle(d=sw_large_hole_diameter);
-            translate([-sw_small_hole_spacing/2, 0]) circle(d=sw_small_hole_diameter);
-            translate([ sw_small_hole_spacing/2, 0]) circle(d=sw_small_hole_diameter);
-        }
-    }
-}
-
-module sw_drill(clearance) {
-    translate([0, 0, -sw_button_z_offset]) {
-        rotate([0, -90, 0]) {
-            linear_extrude(h=10) {
-                minkowski() {
-                    sw_outline();
-                    circle(r=clearance);
-                }
-            }
-        }
-    }
-}
-
-////////////////////////////////////////////////////////
-
-module header_x2(nx, b) {
-    ny = 2;
-    
-    w = 5.08;
-    d = 8.50;
-    
-    pin_spacing_x = 2.54;
-    pin_spacing_y = 2.54;
-    pin_d = 1.02;
-    pin_length = 3.2;
-    
-    rotate([180, 0, 0]) {
-        color("gray") translate([-b/2, -w/2, 0]) linear_extrude(height=d) square([b, w]);
-        
-        pin_tx = nx * pin_spacing_x / -2;
-        pin_ty = ny * pin_spacing_y / -2;
-        translate([pin_tx, pin_ty]) {
-            for(y = [1 : ny]) {
-                for(x = [1 : nx]) {
-                    tx = (x - 0.5) * pin_spacing_x;
-                    ty = (y - 0.5) * pin_spacing_y;
-                    translate([tx, ty]) {
-                        rotate([180, 0]) {
-                            linear_extrude(height=pin_length) {
-                                circle(d=pin_d);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-}
-
-module header_11x2() {
-    nx = 11;
-    b = 28.44;
-    header_x2(nx, b);
-}
-
-module header_13x2() {
-    nx = 13;
-    b = 33.52;
-    header_x2(nx, b);
-}
-
-////////////////////////////////////////////////////////
-
-module usb_plug_poly() {
-    inner_w1 = 6.9;
-    inner_h1 = 1.1;
-    inner_h = 1.85;
-    inner_w2 = 5.4;
-    inner_dw = inner_w1 - inner_w2;
-    
-    translate([-inner_w1/2, 0])
-        polygon(points=[
-            [0, 0],
-            [inner_w1, 0],
-            [inner_w1, inner_h1],
-            [inner_w1 - inner_dw/2, inner_h],
-            [inner_dw/2, inner_h],
-            [0, inner_h1]
-        ]);
-}
-
-module usb_body_outline() {
-    body_buffer_r = 0.3;
-
-    translate([0, body_buffer_r]) {
-        minkowski() {
-            usb_plug_poly();
-            circle(r=body_buffer_r);
-        }
-    }
-}
-
-module usb_plug_outline() {
-    outer_h = 3;
-    outer_ty = (outer_h - 2.45) / 2;
-    outer_buffer_r = 0.6;
-
-    translate([0, outer_ty]) {
-        minkowski() {
-            usb_plug_poly();
-            circle(r=outer_buffer_r);
-        }
-    }
-}
-
-usb_body_h = 2.45;
-usb_body_depth = 5.0;
-
-//usb_outer_w1 = 8;
-usb_outer_depth = 0.63;
-
-module usb_transform() {
-    rotate([90, 180, 270]) translate([0, 0, -usb_outer_depth - 2.15 + 1.65]) children();
-}
-
-module usb() {    
-    color("lightgray") usb_transform() {
-        translate([0, 0, usb_outer_depth]) {
-            linear_extrude(height=usb_body_depth) {
-                usb_body_outline();
-            }
-        }
-
-        linear_extrude(height=usb_outer_depth) {
-            usb_plug_outline();
-        }
-    }
-}
-
-module usb_drill(clearance) {
-    usb_transform() {
-        translate([0, 0, -usb_outer_depth - 10]) {
-            linear_extrude(height=20) {
-                minkowski() {
-                    usb_plug_outline();
-                    circle(r=clearance);
-                }
-            }
-        }
-    }
-}
-
-////////////////////////////////////////////////////////
-
-module pcb_outline() {
-    minkowski() {
-        polygon(
-            points=[[64,104], [176,104], [176,171], [64,171]]
-        );
-        circle(r=pcb_corner_r);
-    }
-}
-
-module pcb_shape() {
-    difference() {
-        pcb_outline();
-        pcb_cutout_antenna();
-        pcb_cutout_clocks();
-        pcb_mounting_hole_drills();
-    }
-}
-
-////////////////////////////////////////////////////////
-
-module hackrf_one_components() {
-    color("green") linear_extrude(height=h1_pcb_thickness) pcb_shape();
-
-    translate([ 61.00, 161.00]) rotate(  0) sma_73251_2120("p4" , h1_pcb_thickness);
-    translate([179.00, 145.00]) rotate(180) sma_73251_2120("p2" , h1_pcb_thickness);
-    translate([179.00, 163.00]) rotate(180) sma_73251_2120("p16", h1_pcb_thickness);
-
-    translate([ 61.00, 117.90]) rotate(-90) led("d7", "green");
-    translate([ 61.27, 130.55]) rotate(-90) led("d8", "yellow");
-    translate([ 61.27, 135.12]) rotate(-90) led("d2", "red");
-    translate([ 61.27, 139.69]) rotate(-90) led("d4", "green");
-    translate([ 61.27, 144.27]) rotate(-90) led("d5", "yellow");
-    translate([ 61.27, 148.84]) rotate(-90) led("d6", "red");
-
-    translate([ 62.70, 111.40]) sw();
-    translate([ 62.70, 124.40]) sw();
-
-    translate([171.76, 143.25]) rotate([0, 0,  90]) header_11x2("p20");
-    translate([152.71, 164.84]) rotate([0, 0, 180]) header_13x2("p22");
-    translate([123.50, 143.25]) rotate([0, 0,  90]) header_11x2("p28");
-
-    translate([180.00, 124.00]) usb();
-}
-
-module hackrf_one_transform() {
-    rotate([180, 0, 0]) translate([-60, -100 - pcb_w])
-        children();
-}
-
-module hackrf_one() {
-    hackrf_one_transform() hackrf_one_components();
-}
-
-////////////////////////////////////////////////////////
-
-module spacer() {
-    outer_d = 0.25 * 25.4;
-    inner_d = 0.140 * 25.4;
-    
-    //inner_d = ?
-    rotate([0, 180, 0]) {
-        color("lightgray") {
-            difference() {
-                linear_extrude(height=spacer_height) {
-                    circle(d=outer_d);
-                }
-                translate([0, 0, -0.5]) {
-                    linear_extrude(height=spacer_height + 1) {
-                        circle(d=inner_d);
-                    }
-                }
-            }
-        }
-    }
-}
-
-module screw() {
-    wrench_sides = 6;
-    wrench_diameter = 2.0 / cos(360 / wrench_sides / 2);
-    
-    head_height = 2.0;
-    head_d = 5.5;
-    
-    shaft_length = 20.0;
-    threaded_d = 3.0;
-    
-    color("gray") {
-        translate([0, 0, -head_height]) difference() {
-            linear_extrude(height=head_height)
-                circle(d=head_d);
-            translate([0, 0, -0.5]) linear_extrude(height=head_height + 1)
-                circle(d=wrench_diameter, $fn=wrench_sides);
-        }
-        linear_extrude(height=shaft_length)
-            circle(d=threaded_d);
-    }
-}
-
-////////////////////////////////////////////////////////
-
-module header_mle_dual(name, nx) {
-    w = nx * 2.54;
-    h = 5;
-    d = 2.54;
-    offset = 3.81 - d;
-    
-    base_h = 7.44;
-    
-    translate([-w/2, -h/2, offset]) {
-        color("gray") linear_extrude(height=d) square([w, h]);
-    }
-    translate([-w/2, -base_h/2]) {
-        color("lightgray") {
-            linear_extrude(height=offset) square([w, base_h]);
-        }
-    }
-}
-
-module lcd_kingtech() {
-    body_w = 42.72;
-    body_h = 60.26;
-    body_d = 2.50;
-    
-    touch_d = 0.7;
-    
-    tape_d = 0.1;
-    
-    view_w = 36.72;
-    view_h = 48.96;
-    view_tx = (body_w - view_w) / 2;
-    view_ty = 1.25 + 2.95;
-    
-    tab_w = 0.7;
-    tab_h = 2.5;
-    tab_d = 0.9;
-    tab_tz = body_d - tab_d;
-    tab_bot_ty = body_h - tab_h;
-    
-    translate([-body_w / 2, -view_ty - view_h/2, -(body_d + touch_d)]) {
-        translate([0, 0, touch_d]) {
-            color("beige") difference() {
-                linear_extrude(height=body_d) {
-                    square([body_w, body_h]);
-                }
-                
-                translate([view_tx, view_ty, -1]) {
-                    linear_extrude(height=2) {
-                        square([view_w, view_h]);
-                    }
-                }
-            }
-
-            color("beige") translate([0, 0, tab_tz]) {
-                linear_extrude(height=tab_d) {
-                    translate([-tab_w, 0]) square([tab_w, tab_h]);
-                    translate([-tab_w, tab_bot_ty]) square([tab_w, tab_h]);
-                    translate([body_w, 0]) square([tab_w, tab_h]);
-                    translate([body_w, tab_bot_ty]) square([tab_w, tab_h]);
-                }
-            }
-
-            color("black") translate([view_tx, view_ty]) {
-                linear_extrude(height=1) {
-                    square([view_w, view_h]);
-                }
-            }
-        }
-        
-        color("lightgray", alpha=0.5) {
-            linear_extrude(height=touch_d) {
-                square([body_w, body_h]);
-            }
-        }
-    }
-}
-
-module control_wheel() {
-    h = 6.0;
-    
-    top_d = 32.0;
-    top_h = 3.0;
-    
-    ring_d = 34.4;
-    ring_h = 0.2;
-    
-    bot_d = ring_d;
-    bot_h = h - ring_h - top_h;
-    
-    translate([0, 0, -h])
-        color("white")
-            linear_extrude(height=top_h)
-                circle(d=top_d);
-    
-    translate([0, 0, -(h - top_h)])
-        color("white")
-            linear_extrude(height=ring_h)
-                circle(d=ring_d);
-    
-    translate([0, 0, -(h - top_h - ring_h)])
-        color("black")
-            linear_extrude(height=bot_h)
-                circle(d=bot_d);
-}
-
-module audio_jack_hole() {
-    hole_outer_d = 5.00;
-
-    circle(d=hole_outer_d);
-}
-
-audio_jack_body_w = 6.00;
-audio_jack_body_h = 5.00;
-audio_jack_body_depth = 15.5;
-
-audio_jack_hole_inner_d = 3.600;
-audio_jack_hole_depth = 1.5;
-
-audio_jack_mounting_offset = 7;
-
-module audio_jack() {
-    color("gray") rotate([90, 0, 0]) {
-        translate([0, audio_jack_body_h/2, -audio_jack_hole_depth - audio_jack_mounting_offset]) {
-            translate([0, 0, audio_jack_hole_depth])
-                linear_extrude(height=audio_jack_body_depth)
-                    square([audio_jack_body_w, audio_jack_body_h], center=true);
-            difference() {
-                linear_extrude(height=audio_jack_hole_depth)
-                    audio_jack_hole();
-                translate([0, 0, -0.5])
-                    linear_extrude(height=audio_jack_hole_depth + 1)
-                        circle(d=audio_jack_hole_inner_d);
-            }
-        }
-    }
-}
-
-module audio_jack_drill(diameter) {
-    translate([0, audio_jack_mounting_offset, audio_jack_body_h/2]) {
-        rotate([-90, 0, 0]) {
-            linear_extrude(height=10) {
-                circle(r=diameter / 2.0);
-            }
-        }
-    }
-}
-
-micro_sd_body_h = 1.32;
-micro_sd_body_w = 13.825;
-micro_sd_body_depth = 15.25;
-
-micro_sd_card_w = 11.0;
-micro_sd_card_h = 1.0;
-micro_sd_card_depth = 15.0;
-
-micro_sd_card_tx = 0.9;
-micro_sd_card_ty = (micro_sd_body_h - micro_sd_card_h) / 2;
-micro_sd_card_insert_depth = micro_sd_card_depth - 2.3;
-
-micro_sd_card_eject_depth = micro_sd_card_depth - (2.3 + 3.3);
-
-module micro_sd() {
-    translate([-micro_sd_body_w/2, -micro_sd_body_depth/2]) {
-        rotate([90, 0, 0]) {
-            color("lightgray") difference() {
-                translate([0, 0, -micro_sd_body_depth])
-                    linear_extrude(height=micro_sd_body_depth)
-                        square([micro_sd_body_w, micro_sd_body_h]);
-                translate([micro_sd_card_tx, micro_sd_card_ty, -micro_sd_card_insert_depth])
-                    linear_extrude(height=micro_sd_card_depth)
-                        square([micro_sd_card_w, micro_sd_card_h]);
-            }
-
-            color("black")
-                translate([micro_sd_card_tx, micro_sd_card_ty, -micro_sd_card_eject_depth])
-                    linear_extrude(height=micro_sd_card_depth)
-                        square([micro_sd_card_w, micro_sd_card_h]);
-        }
-    }
-}
-
-module micro_sd_drill(clearance) {
-    extra_width = 2;
-    translate([-micro_sd_body_w/2, 0]) {
-        rotate([90, 0, 0]) {
-            translate([micro_sd_card_tx - clearance - extra_width, micro_sd_card_ty - clearance, micro_sd_body_depth/2]) {
-                cube([micro_sd_card_w + 2 * clearance + extra_width, micro_sd_card_h + 2 * clearance, 10]);
-            }
-        }
-    }
-}
-
-////////////////////////////////////////////////////////
-
-module portapack_h1_pcb_mounting_hole_drills() {
-    translate([ 64, 104]) pcb_mounting_hole_drill();
-    translate([176, 104]) pcb_mounting_hole_drill();
-    translate([ 64, 171]) pcb_mounting_hole_drill();
-    translate([176, 171]) pcb_mounting_hole_drill();
-}
-
-module portapack_h1_pcb_shape() {
-    difference() {
-        pcb_outline();
-        portapack_h1_pcb_mounting_hole_drills();
-    }
-}
-
-module portapack_h1_pcb() {
-    color("green") linear_extrude(height=pp_h1_pcb_thickness) {
-        portapack_h1_pcb_shape();
-    }
-}
-
-module portapack_h1_components_top() {
-    translate([ 94.83, 137.50]) rotate(90) lcd_kingtech();
-    translate([147.50, 137.50]) control_wheel();
-}
-
-module portapack_h1_components_bottom() {
-    translate([0, 0, pp_h1_pcb_thickness])  {
-        translate([171.76, 143.25]) rotate( 90) header_mle_dual("p20", 11);
-        translate([152.71, 164.84]) rotate(180) header_mle_dual("p22", 13);
-        translate([123.50, 143.25]) rotate( 90) header_mle_dual("p28", 11);
-        translate([172.10, 114.80]) rotate(270) audio_jack();
-        translate([ 68.40, 114.60]) rotate(270) micro_sd();
-    }
-}
-
-module portapack_h1_assembly() {
-    portapack_h1_pcb();
-    portapack_h1_components_top();
-    portapack_h1_components_bottom();
-}
-
-module portapack_h1_transform() {
-    rotate([180, 0, 0]) translate([-60, -100 - pcb_w])
-        children();
-}
-
-module portapack_h1() {
-    portapack_h1_transform() portapack_h1_assembly();
-}
-
-////////////////////////////////////////////////////////
-
-module slot() {
-    hull() {
-        children();
-        translate([0, 0, -20]) children();
-    }
-}
-
-module portapack_h1_drills() {
-    micro_sd_clearance = 0.5;
-    audio_jack_hole_diameter = 7.0;  // 6.5mm + 0.25mm clearance.
-
-    portapack_h1_transform() {
-        translate([172.10, 114.80, pp_h1_pcb_thickness]) rotate(270) audio_jack_drill(audio_jack_hole_diameter);
-        slot() translate([ 68.40, 114.60, pp_h1_pcb_thickness]) rotate(270) micro_sd_drill(micro_sd_clearance);
-    }
-}
-
-module hackrf_one_drills() {
-    clearance = 0.5;
-    sw_clearance = 0.6;
-
-    hackrf_one_transform() {
-        slot() translate([ 61.00, 161.00]) rotate(  0) sma_73251_2120_drill(clearance, h1_pcb_thickness);
-        translate([179.00, 145.00]) rotate(180) sma_73251_2120_drill(clearance, h1_pcb_thickness);
-        translate([179.00, 163.00]) rotate(180) sma_73251_2120_drill(clearance, h1_pcb_thickness);
-
-        translate([ 61.00, 117.90]) rotate(-90) led_drill();
-        translate([ 61.27, 130.55]) rotate(-90) led_drill();
-        translate([ 61.27, 135.12]) rotate(-90) led_drill();
-        translate([ 61.27, 139.69]) rotate(-90) led_drill();
-        translate([ 61.27, 144.27]) rotate(-90) led_drill();
-        translate([ 61.27, 148.84]) rotate(-90) led_drill();
-
-        slot() translate([ 62.70, 111.40]) sw_drill(sw_clearance);
-        slot() translate([ 62.70, 124.40]) sw_drill(sw_clearance);
-
-        translate([180.00, 124.00]) usb_drill(clearance);
-    }
-}
-
-module portapack_h1_stack_hackrf_one() {
-    hackrf_one();
-}
-
-module portapack_h1_stack_spacers() {
-    hackrf_one_transform() {
-        translate([ 64, 104]) spacer();
-        translate([176, 104]) spacer();
-        translate([ 64, 171]) spacer();
-        translate([176, 171]) spacer();
-    }    
-}
-
-module portapack_h1_stack_portapack() {
-    translate([0, 0, spacer_height + pp_h1_pcb_thickness]) portapack_h1();
-}
-
-module portapack_h1_stack_screws() {
-    screw_tz = spacer_height + pp_h1_pcb_thickness;
-    translate([0, 0, screw_tz]) portapack_h1_transform() {
-        translate([ 64, 104]) screw();
-        translate([176, 104]) screw();
-        translate([ 64, 171]) screw();
-        translate([176, 171]) screw();
-    }
-}
-
-module portapack_h1_stack() {
-    portapack_h1_stack_hackrf_one();
-    portapack_h1_stack_spacers();
-    portapack_h1_stack_portapack();
-    portapack_h1_stack_screws();
-}
-
-module portapack_h1_stack_drills() {
-    hackrf_one_drills();
-
-    translate([0, 0, spacer_height + pp_h1_pcb_thickness]) portapack_h1_drills();
-}