mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-09-19 20:44:40 -04:00
SSTV transmit beta (320x256 24bpp Scottie 2 only)
This commit is contained in:
parent
5b74b83458
commit
6a0bcb9cca
13 changed files with 410 additions and 45 deletions
|
@ -91,6 +91,14 @@ void set_tones_data(const uint32_t bw, const uint32_t pre_silence, const uint16_
|
|||
send_message(&message);
|
||||
}
|
||||
|
||||
void set_sstv_data(const uint8_t vis_code, const uint32_t pixel_duration) {
|
||||
const SSTVConfigureMessage message {
|
||||
vis_code,
|
||||
pixel_duration
|
||||
};
|
||||
send_message(&message);
|
||||
}
|
||||
|
||||
void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phase_inc_mark, const uint32_t afsk_phase_inc_space,
|
||||
const uint8_t afsk_repeat, const uint32_t afsk_bw, const bool afsk_alt_format) {
|
||||
const AFSKConfigureMessage message {
|
||||
|
|
|
@ -57,6 +57,7 @@ struct WFMConfig {
|
|||
|
||||
void set_tones_data(const uint32_t bw, const uint32_t pre_silence, const uint16_t tone_count,
|
||||
const bool dual_tone, const bool audio_out);
|
||||
void set_sstv_data(const uint8_t vis_code, const uint32_t pixel_duration);
|
||||
void set_audiotx_data(const uint32_t divider, const uint32_t bw, const uint32_t gain_x10,
|
||||
const bool ctcss_enabled, const uint32_t ctcss_phase_inc);
|
||||
void set_fifo_data(const int8_t * data);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
// Color bitmaps generated with:
|
||||
// Gimp image > indexed colors (16), then "xxd -i *.bmp"
|
||||
|
||||
//BUG: Length is wrong in soundboard for long files (> 1min ?)
|
||||
//BUG: RDS Radiotext is not recognized in Redsea (and car radio)
|
||||
//BUG: RDS doesn't stop baseband when stopping tx ?
|
||||
//BUG: Check AFSK transmit end, skips last bits ?
|
||||
|
|
|
@ -361,7 +361,7 @@ SystemMenuView::SystemMenuView(NavigationView& nav) {
|
|||
{ "Replay", ui::Color::grey(), &bitmap_icon_replay, [&nav](){ nav.push<NotImplementedView>(); } },
|
||||
{ "Audio transmitters", ui::Color::green(), &bitmap_icon_audiotx, [&nav](){ nav.push<TransmitterAudioMenuView>(); } },
|
||||
{ "Code transmitters", ui::Color::green(), &bitmap_icon_codetx, [&nav](){ nav.push<TransmitterCodedMenuView>(); } },
|
||||
{ "SSTV transmitter", ui::Color::grey(), &bitmap_icon_sstv, [&nav](){ nav.push<SSTVTXView>(); } },
|
||||
{ "SSTV transmitter", ui::Color::dark_orange(), &bitmap_icon_sstv, [&nav](){ nav.push<SSTVTXView>(); } },
|
||||
{ "Close Call", ui::Color::cyan(), &bitmap_icon_closecall, [&nav](){ nav.push<CloseCallView>(); } },
|
||||
{ "Jammer", ui::Color::orange(),&bitmap_icon_jammer, [&nav](){ nav.push<JammerView>(); } },
|
||||
{ "Utilities", ui::Color::purple(),&bitmap_icon_utilities, [&nav](){ nav.push<UtilitiesView>(); } },
|
||||
|
|
|
@ -68,7 +68,6 @@ 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;
|
||||
|
||||
|
@ -97,28 +96,22 @@ 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.
|
||||
void SSTVTXView::prepare_scanline() {
|
||||
sstv_scanline scanline_buffer;
|
||||
uint32_t component, pixel_idx;
|
||||
|
||||
const uint8_t VIS_code = 0b00011101; // Scottie 2
|
||||
if (scanline_counter >= (256 * 3)) {
|
||||
progressbar.set_value(0);
|
||||
transmitter_model.disable();
|
||||
options_bitmaps.set_focusable(true);
|
||||
tx_view.set_transmitting(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// 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:
|
||||
progressbar.set_value(scanline_counter);
|
||||
|
||||
// Scottie 2 scanline:
|
||||
// (First line: 1200 9ms)
|
||||
// 1500 1.5ms
|
||||
// Green
|
||||
// 1500 1.5ms
|
||||
|
@ -126,27 +119,71 @@ void SSTVTXView::start_tx() {
|
|||
// 1200 9ms
|
||||
// 1500 1.5ms
|
||||
// Red
|
||||
// Scanline time: 88.064ms (.2752ms/pixel @ 320 pixels/line)
|
||||
// Scanline time: 88.064ms (275.2us/pixel @ 320 pixels/line)
|
||||
|
||||
transmitter_model.set_sampling_rate(1536000U);
|
||||
component = scanline_counter % 3;
|
||||
|
||||
if ((!scanline_counter) || (component == 2)) {
|
||||
scanline_buffer.start_tone.frequency = SSTV_F2D(1200);
|
||||
scanline_buffer.start_tone.duration = SSTV_MS2S(9);
|
||||
} else
|
||||
scanline_buffer.start_tone.duration = 0;
|
||||
|
||||
scanline_buffer.gap_tone.frequency = SSTV_F2D(1500);
|
||||
scanline_buffer.gap_tone.duration = SSTV_MS2S(1.5);
|
||||
|
||||
if (component == 0) {
|
||||
// Read a new line
|
||||
read_boundary(pixels_buffer,
|
||||
bmp_header.image_data + ((255 - (scanline_counter / 3)) * sizeof(pixels_buffer)),
|
||||
sizeof(pixels_buffer));
|
||||
}
|
||||
|
||||
for (uint32_t bmp_px = 0; bmp_px < 320; bmp_px++) {
|
||||
pixel_idx = bmp_px * 3;
|
||||
if (component == 0)
|
||||
scanline_buffer.luma[bmp_px] = pixels_buffer[pixel_idx + 1]; // Green
|
||||
else if (component == 1)
|
||||
scanline_buffer.luma[bmp_px] = pixels_buffer[pixel_idx]; // Blue
|
||||
else
|
||||
scanline_buffer.luma[bmp_px] = pixels_buffer[pixel_idx + 2]; // Red
|
||||
}
|
||||
|
||||
baseband::set_fifo_data((int8_t *)&scanline_buffer);
|
||||
|
||||
scanline_counter++;
|
||||
}
|
||||
|
||||
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. See sstv.hpp:
|
||||
|
||||
// Scottie 2 is 320x256 px
|
||||
|
||||
scanline_counter = 0;
|
||||
prepare_scanline();
|
||||
|
||||
transmitter_model.set_sampling_rate(3072000U);
|
||||
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
|
||||
);*/
|
||||
baseband::set_sstv_data(
|
||||
0b00011101, // Scottie 2, 275.2us/px
|
||||
(uint32_t)(0.0002752 * 3072000.0)
|
||||
);
|
||||
|
||||
// Todo: Find a better way to prevent user from changing bitmap during tx
|
||||
options_bitmaps.set_focusable(false);
|
||||
tx_view.focus();
|
||||
}
|
||||
|
||||
void SSTVTXView::on_bitmap_changed(size_t index) {
|
||||
bmp_file.open("/sstv/" + bitmaps[index].string());
|
||||
bmp_file.read(&bmp_header, sizeof(bmp_header));
|
||||
progressbar.set_max(256 * 3);
|
||||
set_dirty();
|
||||
}
|
||||
|
||||
|
@ -158,7 +195,6 @@ SSTVTXView::SSTVTXView(
|
|||
using option_t = std::pair<std::string, int32_t>;
|
||||
using options_t = std::vector<option_t>;
|
||||
options_t bitmap_options;
|
||||
uint32_t c;
|
||||
|
||||
// Search for valid bitmaps
|
||||
file_list = scan_root_files(u"/sstv", u"*.bmp");
|
||||
|
@ -184,12 +220,16 @@ SSTVTXView::SSTVTXView(
|
|||
return;
|
||||
}
|
||||
|
||||
//baseband::run_image(portapack::spi_flash::image_tag_sstv_tx);
|
||||
// Maybe this could be merged with proc_tones ? Pretty much the same except lots
|
||||
// of different tones (256+)
|
||||
baseband::run_image(portapack::spi_flash::image_tag_sstv_tx);
|
||||
|
||||
add_children({
|
||||
&labels,
|
||||
&options_bitmaps,
|
||||
&text_mode
|
||||
&text_mode,
|
||||
&progressbar,
|
||||
&tx_view
|
||||
});
|
||||
|
||||
for (const auto& bitmap : bitmaps)
|
||||
|
@ -200,6 +240,26 @@ SSTVTXView::SSTVTXView(
|
|||
this->on_bitmap_changed(i);
|
||||
};
|
||||
options_bitmaps.set_selected_index(0);
|
||||
on_bitmap_changed(0);
|
||||
|
||||
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]() {
|
||||
start_tx();
|
||||
tx_view.set_transmitting(true);
|
||||
};
|
||||
|
||||
tx_view.on_stop = [this]() {
|
||||
baseband::set_sstv_data(0, 0);
|
||||
tx_view.set_transmitting(false);
|
||||
transmitter_model.disable();
|
||||
options_bitmaps.set_focusable(true);
|
||||
};
|
||||
}
|
||||
|
||||
} /* namespace ui */
|
||||
|
|
|
@ -47,20 +47,25 @@ public:
|
|||
void focus() override;
|
||||
void paint(Painter&) override;
|
||||
|
||||
std::string title() const override { return "SSTV transmit"; };
|
||||
std::string title() const override { return "SSTV TX (beta)"; };
|
||||
|
||||
private:
|
||||
NavigationView& nav_;
|
||||
|
||||
sstv_scanline scanline_buffer { };
|
||||
|
||||
File bmp_file { };
|
||||
bmp_header_t bmp_header { };
|
||||
std::vector<std::filesystem::path> bitmaps { };
|
||||
bool file_error { false };
|
||||
uint32_t scanline_counter { 0 };
|
||||
uint8_t pixels_buffer[320 * 3]; // 320 pixels @ 24bpp
|
||||
|
||||
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();
|
||||
void prepare_scanline();
|
||||
|
||||
Labels labels {
|
||||
{ { 1 * 8, 1 * 8 }, "File:", Color::light_grey() }
|
||||
|
@ -73,7 +78,7 @@ private:
|
|||
};
|
||||
Text text_mode {
|
||||
{ 2 * 8, 4 * 8, 16 * 8, 16 },
|
||||
"Scottie 2 (beta)"
|
||||
"Scottie 2"
|
||||
};
|
||||
|
||||
ProgressBar progressbar {
|
||||
|
@ -86,15 +91,15 @@ private:
|
|||
12
|
||||
};
|
||||
|
||||
/*MessageHandlerRegistration message_handler_fifo_signal {
|
||||
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->prepare_audio();
|
||||
this->prepare_scanline();
|
||||
}
|
||||
}
|
||||
};*/
|
||||
};
|
||||
};
|
||||
|
||||
} /* namespace ui */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue