diff --git a/firmware/application/apps/lge_app.cpp b/firmware/application/apps/lge_app.cpp new file mode 100644 index 00000000..ac2b822e --- /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 +#include + +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 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(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 00000000..0581b9bf --- /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(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 7d55213d..52206b25 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 20ac4af6..dde7a394 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 f8ed83cf..00000000 --- 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(); - 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( - 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(receiver_model.lna()), - static_cast(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(); - 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((n % 3) * 78 + 3), - static_cast((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(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 5f9e65b0..00000000 --- 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 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 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(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(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(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 0c05f0cc..a4950deb 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 9d460da9..1e5703e1 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 997c0e4d..504ce9d6 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(); } }, { "Jammer", ui::Color::yellow(), &bitmap_icon_jammer, [&nav](){ nav.push(); } }, { "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push(); } }, + { "LGE tool", ui::Color::yellow(), &bitmap_icon_lge, [&nav](){ nav.push(); } }, { "Microphone", ui::Color::green(), &bitmap_icon_microphone, [&nav](){ nav.push(); } }, { "Morse code", ui::Color::green(), &bitmap_icon_morse, [&nav](){ nav.push(); } }, { "Burger pagers", ui::Color::yellow(), &bitmap_icon_burger, [&nav](){ nav.push(); } }, diff --git a/firmware/baseband/proc_fsk.cpp b/firmware/baseband/proc_fsk.cpp index f5f56588..1b4f9f5f 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; - shared_memory.application_queue.push(txprogress_message); - } else { - progress_count++; - } - } - sample_count = 0; - } else { - sample_count++; - } - 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 { + 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++; + } + 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 b00b4507..3235cef1 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 00000000..3aa05c00 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 83880019..00000000 --- a/hardware/portapack_h1/case/pp_h1_case_2.scad +++ /dev/null @@ -1,80 +0,0 @@ -use -//use -use -include - -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 113f7cb7..00000000 --- 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 2b93f437..00000000 --- a/hardware/portapack_h1/case/pp_h1_shell.scad +++ /dev/null @@ -1,150 +0,0 @@ -include - -$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 341831ae..00000000 --- a/hardware/portapack_h1/case/pp_h1_stack.scad +++ /dev/null @@ -1,841 +0,0 @@ -include - -$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(); -}