diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index b61c2227b..bddc38dbc 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -188,6 +188,7 @@ set(CPPSRC ui_setup.cpp ui_soundboard.cpp ui_spectrum.cpp + ui_sstvtx.cpp ui_textentry.cpp ui_touch_calibration.cpp ui_transmitter.cpp diff --git a/firmware/application/bitmap.hpp b/firmware/application/bitmap.hpp index 021956839..501980021 100644 --- a/firmware/application/bitmap.hpp +++ b/firmware/application/bitmap.hpp @@ -1079,6 +1079,28 @@ static constexpr Bitmap bitmap_icon_audiotx { { 16, 16 }, bitmap_icon_audiotx_data }; +static constexpr uint8_t bitmap_icon_sstv_data[] = { + 0x10, 0x08, + 0x20, 0x04, + 0x40, 0x02, + 0x80, 0x01, + 0xFF, 0xFF, + 0x03, 0xF8, + 0x5D, 0xF0, + 0x05, 0xD0, + 0x01, 0xF0, + 0x01, 0xD0, + 0x01, 0xF0, + 0x01, 0xD0, + 0x01, 0xF0, + 0x01, 0x90, + 0x03, 0x98, + 0xFF, 0xFF, +}; +static constexpr Bitmap bitmap_icon_sstv { + { 16, 16 }, bitmap_icon_sstv_data +}; + static constexpr uint8_t bitmap_icon_jammer_data[] = { 0xA6, 0x2C, 0x73, 0x47, diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index aec15a9a1..fb576bc0a 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -49,6 +49,7 @@ #include "ui_sd_wipe.hpp" #include "ui_setup.hpp" #include "ui_soundboard.hpp" +#include "ui_sstvtx.hpp" #include "ui_whipcalc.hpp" #include "ui_whistle.hpp" @@ -360,8 +361,8 @@ SystemMenuView::SystemMenuView(NavigationView& nav) { { "Replay", ui::Color::grey(), &bitmap_icon_replay, [&nav](){ nav.push(); } }, { "Audio transmitters", ui::Color::green(), &bitmap_icon_audiotx, [&nav](){ nav.push(); } }, { "Code transmitters", ui::Color::green(), &bitmap_icon_codetx, [&nav](){ nav.push(); } }, - { "SSTV transmitter", ui::Color::grey(), nullptr, [&nav](){ nav.push(); } }, - { "Close Call", ui::Color::orange(),&bitmap_icon_closecall, [&nav](){ nav.push(); } }, + { "SSTV transmitter", ui::Color::grey(), &bitmap_icon_sstv, [&nav](){ nav.push(); } }, + { "Close Call", ui::Color::cyan(), &bitmap_icon_closecall, [&nav](){ nav.push(); } }, { "Jammer", ui::Color::orange(),&bitmap_icon_jammer, [&nav](){ nav.push(); } }, { "Utilities", ui::Color::purple(),&bitmap_icon_utilities, [&nav](){ nav.push(); } }, { "Setup", ui::Color::white(), &bitmap_icon_setup, [&nav](){ nav.push(); } }, diff --git a/firmware/application/ui_numbers.cpp b/firmware/application/ui_numbers.cpp index f6ad29efe..382e94989 100644 --- a/firmware/application/ui_numbers.cpp +++ b/firmware/application/ui_numbers.cpp @@ -224,6 +224,7 @@ NumbersStationView::NumbersStationView( baseband::run_image(portapack::spi_flash::image_tag_audio_tx); add_children({ + &labels, &symfield_code, &check_armed, &options_voices, @@ -233,7 +234,7 @@ NumbersStationView::NumbersStationView( }); for (const auto& voice : voices) - voice_options.emplace_back(voice.dir.substr(0, 4), c); + voice_options.emplace_back(voice.dir.substr(0, 4), 0); options_voices.set_options(voice_options); options_voices.on_change = [this](size_t i, int32_t) { diff --git a/firmware/application/ui_sstvtx.cpp b/firmware/application/ui_sstvtx.cpp new file mode 100644 index 000000000..84785f0eb --- /dev/null +++ b/firmware/application/ui_sstvtx.cpp @@ -0,0 +1,205 @@ +/* + * 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. + */ + +#include "ui_sstvtx.hpp" +#include "string_format.hpp" + +#include "portapack.hpp" +#include "hackrf_hal.hpp" + +#include +#include + +using namespace portapack; + +namespace ui { + +void SSTVTXView::focus() { + if (file_error) + nav_.display_modal("No files", "No valid bitmaps\nin /sstv directory.", ABORT, nullptr); + else + options_bitmaps.focus(); +} + +void SSTVTXView::read_boundary(uint8_t * const buffer, uint32_t position, uint32_t length) { + uint32_t to_read, split; + uint8_t * buffer_copy = buffer; + + bmp_file.seek(position); + + // FatFS bug workaround: we can't read across 512 byte boundaries ?! + while (length) { + to_read = (length <= 512) ? length : 512; + if ((position & 512) != ((position + to_read - 1) & 512)) { + // Crossing: do 2 reads before and after the boundary + split = 512 - (position & 511); + bmp_file.read(buffer_copy, split); + bmp_file.read(buffer_copy + split, to_read - split); + } else { + // No crossing, just read normally + bmp_file.read(buffer_copy, to_read); + } + position += to_read; + buffer_copy += to_read; + length -= to_read; + } +} + +void SSTVTXView::paint(Painter&) { + ui::Color line_buffer[160]; + Coord line; + uint32_t data_idx, bmp_px, pixel_idx; + uint8_t pixels_buffer[320 * 3]; // 320 pixels @ 24bpp + + data_idx = bmp_header.image_data; + + for (line = 0; line < (256 / 2); line++) { + + // Buffer a whole line + read_boundary(pixels_buffer, data_idx, sizeof(pixels_buffer)); + + for (bmp_px = 0; bmp_px < 160; bmp_px++) { + pixel_idx = bmp_px * 3 * 2; + line_buffer[bmp_px] = Color(pixels_buffer[pixel_idx + 2], + pixels_buffer[pixel_idx + 1], + pixels_buffer[pixel_idx + 0]); + } + portapack::display.render_line({ 16, 80 + 128 - line }, 160, line_buffer); + data_idx += sizeof(pixels_buffer) * 2; + } +} + +SSTVTXView::~SSTVTXView() { + transmitter_model.disable(); + baseband::shutdown(); +} + +void SSTVTXView::on_tuning_frequency_changed(rf::Frequency f) { + transmitter_model.set_tuning_frequency(f); +} + +void SSTVTXView::start_tx() { + // Baseband SSTV TX code should have a 2 scanlines buffer, and ask + // for fill-up when there's 1 or less remaining. This should leave + // enough time for the code here to generate the scanline data + // before tx. + + const uint8_t VIS_code = 0b00011101; // Scottie 2 + + // Calibration: + // 1900 300ms + // 1200 10ms + // 1900 300ms + // VIS: (30ms * 10 = 300ms) + // 1200 + // 00011101 (0=1300, 1=1100) + // 1200 + + // Scottie 2: 320x256 px + + // V-sync ? + // First line: 1200 9ms + // Scanline: + // 1500 1.5ms + // Green + // 1500 1.5ms + // Blue + // 1200 9ms + // 1500 1.5ms + // Red + // Scanline time: 88.064ms (.2752ms/pixel @ 320 pixels/line) + + transmitter_model.set_sampling_rate(1536000U); + transmitter_model.set_rf_amp(true); + //transmitter_model.set_lna(40); + //transmitter_model.set_vga(40); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + /*baseband::set_sstvtx_data( + (1536000 / 44100) - 1, + 12000, + 1, + false, + 0 + );*/ +} + +void SSTVTXView::on_bitmap_changed(size_t index) { + bmp_file.open("/sstv/" + bitmaps[index].string()); + bmp_file.read(&bmp_header, sizeof(bmp_header)); + set_dirty(); +} + +SSTVTXView::SSTVTXView( + NavigationView& nav +) : nav_ (nav) +{ + std::vector file_list; + using option_t = std::pair; + using options_t = std::vector; + options_t bitmap_options; + uint32_t c; + + // Search for valid bitmaps + file_list = scan_root_files(u"/sstv", u"*.bmp"); + if (!file_list.size()) { + file_error = true; + return; + } + for (const auto& file_name : file_list) { + if (!bmp_file.open("/sstv/" + file_name.string()).is_valid()) { + bmp_file.read(&bmp_header, sizeof(bmp_header)); + if ((bmp_header.signature == 0x4D42) && + (bmp_header.width == 320) && // Must be == 320x256 pixels for now + (bmp_header.height == 256) && + (bmp_header.planes == 1) && + (bmp_header.bpp >= 24) && // 24 or 32 bpp + (bmp_header.compression == 0)) { // No compression + bitmaps.push_back(file_name); + } + } + } + if (!bitmaps.size()) { + file_error = true; + return; + } + + //baseband::run_image(portapack::spi_flash::image_tag_sstv_tx); + + add_children({ + &labels, + &options_bitmaps, + &text_mode + }); + + for (const auto& bitmap : bitmaps) + bitmap_options.emplace_back(bitmap.string().substr(0, 16), 0); + + options_bitmaps.set_options(bitmap_options); + options_bitmaps.on_change = [this](size_t i, int32_t) { + this->on_bitmap_changed(i); + }; + options_bitmaps.set_selected_index(0); +} + +} /* namespace ui */ diff --git a/firmware/application/ui_sstvtx.hpp b/firmware/application/ui_sstvtx.hpp new file mode 100644 index 000000000..fa9899703 --- /dev/null +++ b/firmware/application/ui_sstvtx.hpp @@ -0,0 +1,102 @@ +/* + * 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_SSTVTX_H__ +#define __UI_SSTVTX_H__ + +#include "ui.hpp" +#include "ui_widget.hpp" +#include "ui_receiver.hpp" +#include "ui_navigation.hpp" +#include "ui_font_fixed_8x16.hpp" +#include "baseband_api.hpp" +#include "ui_transmitter.hpp" +#include "message.hpp" +#include "sstv.hpp" +#include "file.hpp" +#include "bmp.hpp" + +using namespace sstv; + +namespace ui { + +class SSTVTXView : public View { +public: + SSTVTXView(NavigationView& nav); + ~SSTVTXView(); + + void focus() override; + void paint(Painter&) override; + + std::string title() const override { return "SSTV transmit"; }; + +private: + NavigationView& nav_; + + File bmp_file { }; + bmp_header_t bmp_header { }; + std::vector bitmaps { }; + bool file_error { false }; + + void read_boundary(uint8_t * buffer, uint32_t position, uint32_t length); + void on_bitmap_changed(size_t index); + void on_tuning_frequency_changed(rf::Frequency f); + void start_tx(); + + Labels labels { + { { 1 * 8, 1 * 8 }, "File:", Color::light_grey() } + }; + + OptionsField options_bitmaps { + { 6 * 8, 1 * 8 }, + 16, + { } + }; + Text text_mode { + { 2 * 8, 4 * 8, 16 * 8, 16 }, + "Scottie 2 (beta)" + }; + + ProgressBar progressbar { + { 16, 25 * 8, 208, 16 } + }; + + TransmitterView tx_view { + 16 * 16, + 10000, + 12 + }; + + /*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->prepare_audio(); + } + } + };*/ +}; + +} /* namespace ui */ + +#endif/*__UI_SSTVTX_H__*/ diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index 9efe1bda9..21d4f1f62 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -275,6 +275,20 @@ macro(DeclareTargets chunk_tag name) set(BASEBAND_IMAGES ${BASEBAND_IMAGES} ${PROJECT_NAME}.img) endmacro() +### ADS-B TX + +set(MODE_CPPSRC + proc_adsbtx.cpp +) +DeclareTargets(PADS ads) + +### AFSK + +set(MODE_CPPSRC + proc_afsk.cpp +) +DeclareTargets(PAFS afsk) + ### AIS set(MODE_CPPSRC @@ -289,6 +303,13 @@ set(MODE_CPPSRC ) DeclareTargets(PAMA am_audio) +### Audio transmit + +set(MODE_CPPSRC + proc_audiotx.cpp +) +DeclareTargets(PATX audio_tx) + ### Capture set(MODE_CPPSRC @@ -310,48 +331,6 @@ set(MODE_CPPSRC ) DeclareTargets(PFSK fsktx) -### POCSAG RX - -set(MODE_CPPSRC - proc_pocsag.cpp -) -DeclareTargets(PPOC pocsag) - -### NFM Audio - -set(MODE_CPPSRC - proc_nfm_audio.cpp -) -DeclareTargets(PNFM nfm_audio) - -### TPMS - -set(MODE_CPPSRC - proc_tpms.cpp -) -DeclareTargets(PTPM tpms) - -### WFM Audio - -set(MODE_CPPSRC - proc_wfm_audio.cpp -) -DeclareTargets(PWFM wfm_audio) - -### Wideband Spectrum - -set(MODE_CPPSRC - proc_wideband_spectrum.cpp -) -DeclareTargets(PSPE wideband_spectrum) - -### OOK - -set(MODE_CPPSRC - proc_ook.cpp -) -DeclareTargets(POOK ook) - ### Jammer set(MODE_CPPSRC @@ -359,13 +338,6 @@ set(MODE_CPPSRC ) DeclareTargets(PJAM jammer) -### Audio transmit - -set(MODE_CPPSRC - proc_audiotx.cpp -) -DeclareTargets(PATX audio_tx) - ### Microphone transmit set(MODE_CPPSRC @@ -373,12 +345,40 @@ set(MODE_CPPSRC ) DeclareTargets(PMTX mic_tx) -### AFSK +### NFM Audio set(MODE_CPPSRC - proc_afsk.cpp + proc_nfm_audio.cpp ) -DeclareTargets(PAFS afsk) +DeclareTargets(PNFM nfm_audio) + +### No op + +set(MODE_CPPSRC + proc_noop.cpp +) +DeclareTargets(PNOP no_operation) + +### OOK + +set(MODE_CPPSRC + proc_ook.cpp +) +DeclareTargets(POOK ook) + +### POCSAG RX + +set(MODE_CPPSRC + proc_pocsag.cpp +) +DeclareTargets(PPOC pocsag) + +### RDS + +set(MODE_CPPSRC + proc_rds.cpp +) +DeclareTargets(PRDS rds) ### Replay @@ -394,27 +394,26 @@ set(MODE_CPPSRC ) DeclareTargets(PTON tones) -### RDS +### TPMS set(MODE_CPPSRC - proc_rds.cpp + proc_tpms.cpp ) -DeclareTargets(PRDS rds) +DeclareTargets(PTPM tpms) -### ADS-B TX +### Wideband Spectrum set(MODE_CPPSRC - proc_adsbtx.cpp + proc_wideband_spectrum.cpp ) -DeclareTargets(PADS ads) +DeclareTargets(PSPE wideband_spectrum) - -### No op +### WFM Audio set(MODE_CPPSRC - proc_noop.cpp + proc_wfm_audio.cpp ) -DeclareTargets(PNOP no_operation) +DeclareTargets(PWFM wfm_audio) ### HackRF "factory" firmware diff --git a/firmware/common/sstv.hpp b/firmware/common/sstv.hpp new file mode 100644 index 000000000..19af77725 --- /dev/null +++ b/firmware/common/sstv.hpp @@ -0,0 +1,37 @@ +/* + * 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. + */ + +namespace sstv { + +struct sstv_tone { + uint16_t frequency; + uint16_t duration; +}; + +struct sstv_scanline { + sstv_tone start_tone; + sstv_tone gap_tone; + uint8_t luma[320]; +}; + +} /* namespace sstv */ + diff --git a/firmware/graphics/icon_sstv.png b/firmware/graphics/icon_sstv.png new file mode 100644 index 000000000..b448fe0ef Binary files /dev/null and b/firmware/graphics/icon_sstv.png differ diff --git a/firmware/portapack-h1-havoc.bin b/firmware/portapack-h1-havoc.bin index b66c4f6c9..7e7a4e42f 100644 Binary files a/firmware/portapack-h1-havoc.bin and b/firmware/portapack-h1-havoc.bin differ diff --git a/screenshots.png b/screenshots.png index b05f2d1bf..4c8808427 100644 Binary files a/screenshots.png and b/screenshots.png differ