Added LGE app, nothing to see here

Update button in signal gen now works for shape change
This commit is contained in:
furrtek 2019-02-06 17:34:53 +00:00
parent e7c0fa394b
commit 162cb4c9fa
16 changed files with 376 additions and 1709 deletions

View File

@ -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 */

View File

@ -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 */

View File

@ -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) {

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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__*/

View File

@ -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,

View File

@ -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

View File

@ -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>(); } },

View File

@ -32,10 +32,9 @@ 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 (configured) {
if (sample_count >= samples_per_bit) {
if (bit_pos > length) {
// End of data
@ -60,7 +59,6 @@ void FSKProcessor::execute(const buffer_c8_t& buffer) {
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;

View File

@ -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;

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}