From d95bda65cefd57ed00d1ae6932f87e4123782716 Mon Sep 17 00:00:00 2001 From: Erwin Ried <1091420+eried@users.noreply.github.com> Date: Mon, 20 Apr 2020 06:45:28 +0200 Subject: [PATCH 1/3] Nrf24l01 demodulation (#338) * NRF demodulation * Update ui_navigation.cpp --- firmware/application/CMakeLists.txt | 1 + firmware/application/apps/ui_nrf_rx.cpp | 165 ++++++++++++++ firmware/application/apps/ui_nrf_rx.hpp | 107 +++++++++ firmware/application/baseband_api.cpp | 10 + firmware/application/baseband_api.hpp | 1 + firmware/application/bitmap.hpp | 22 ++ firmware/application/ui_navigation.cpp | 2 + firmware/baseband/CMakeLists.txt | 6 + firmware/baseband/proc_nrfrx.cpp | 286 ++++++++++++++++++++++++ firmware/baseband/proc_nrfrx.hpp | 96 ++++++++ firmware/common/message.hpp | 22 ++ firmware/common/spi_image.hpp | 1 + 12 files changed, 719 insertions(+) create mode 100644 firmware/application/apps/ui_nrf_rx.cpp create mode 100644 firmware/application/apps/ui_nrf_rx.hpp create mode 100644 firmware/baseband/proc_nrfrx.cpp create mode 100644 firmware/baseband/proc_nrfrx.hpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 91fec2fa..851e021d 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -217,6 +217,7 @@ set(CPPSRC apps/ui_adsb_rx.cpp apps/ui_adsb_tx.cpp apps/ui_afsk_rx.cpp + apps/ui_nrf_rx.cpp apps/ui_aprs_tx.cpp apps/ui_bht_tx.cpp apps/ui_coasterp.cpp diff --git a/firmware/application/apps/ui_nrf_rx.cpp b/firmware/application/apps/ui_nrf_rx.cpp new file mode 100644 index 00000000..32982ae6 --- /dev/null +++ b/firmware/application/apps/ui_nrf_rx.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2017 Furrtek + * Copyright (C) 2020 Shao + * + * 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_nrf_rx.hpp" +#include "ui_modemsetup.hpp" + +#include "modems.hpp" +#include "audio.hpp" +#include "rtc_time.hpp" +#include "baseband_api.hpp" +#include "string_format.hpp" +#include "portapack_persistent_memory.hpp" + +using namespace portapack; +using namespace modems; + +namespace ui { + +void NRFRxView::focus() { + field_frequency.focus(); +} + +void NRFRxView::update_freq(rf::Frequency f) { + receiver_model.set_tuning_frequency(f); +} + +NRFRxView::NRFRxView(NavigationView& nav) { + baseband::run_image(portapack::spi_flash::image_tag_nrf_rx); + + add_children({ + &rssi, + &channel, + &field_rf_amp, + &field_lna, + &field_vga, + &field_frequency, + &text_debug, + &button_modem_setup, + &record_view, + &console + }); + + // DEBUG + record_view.on_error = [&nav](std::string message) { + nav.display_modal("Error", message); + }; + record_view.set_sampling_rate(24000); + + // Auto-configure modem for LCR RX (will be removed later) + update_freq(2480000000); + auto def_bell202 = &modem_defs[0]; + persistent_memory::set_modem_baudrate(def_bell202->baudrate); + serial_format_t serial_format; + serial_format.data_bits = 7; + serial_format.parity = EVEN; + serial_format.stop_bits = 1; + serial_format.bit_order = LSB_FIRST; + persistent_memory::set_serial_format(serial_format); + + field_frequency.set_value(receiver_model.tuning_frequency()); + field_frequency.set_step(100); + field_frequency.on_change = [this](rf::Frequency f) { + update_freq(f); + }; + field_frequency.on_edit = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + update_freq(f); + field_frequency.set_value(f); + }; + }; + + button_modem_setup.on_select = [&nav](Button&) { + nav.push(); + }; + + + // Auto-configure modem for LCR RX (will be removed later) + baseband::set_nrf(persistent_memory::modem_baudrate(), 8, 0, false); + + audio::set_rate(audio::Rate::Hz_24000); + audio::output::start(); + + receiver_model.set_sampling_rate(4000000); + receiver_model.set_baseband_bandwidth(4000000); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + receiver_model.enable(); +} + +void NRFRxView::on_data(uint32_t value, bool is_data) { + //std::string str_console = "\x1B"; + std::string str_console = ""; + if (is_data) { + // Colorize differently after message splits + //str_console += (char)((console_color & 3) + 9); + + //value &= 0xFF; // ABCDEFGH + //value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); // EFGHABCD + //value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); // GHEFCDAB + //value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); // HGFEDCBA + //value &= 0x7F; // Ignore parity, which is the MSB now + + //if ((value >= 32) && (value < 127)) { + // str_console += (char)value; // Printable + //} + + //str_console += (char)'A'; + //str_console += (char)value; + //str_console += "[" + to_string_hex(value, 2) + "]"; + str_console += " " + to_string_hex(value, 2) ; + console.write(str_console); + + + + /*if ((value != 0x7F) && (prev_value == 0x7F)) { + // Message split + console.writeln(""); + console_color++; + + + }*/ + //prev_value = value; + } else { + // Baudrate estimation + //text_debug.set("~" + to_string_dec_uint(value)); + if (value == 'A') + {console.write("addr:");} + else if (value == 'B') + {console.write(" data:");} + else if (value == 'C') + { + console.writeln(""); + console.writeln(""); + } + //console.writeln(""); + } +} + +NRFRxView::~NRFRxView() { + audio::output::stop(); + receiver_model.disable(); + baseband::shutdown(); +} + +} /* namespace ui */ diff --git a/firmware/application/apps/ui_nrf_rx.hpp b/firmware/application/apps/ui_nrf_rx.hpp new file mode 100644 index 00000000..4f72b25b --- /dev/null +++ b/firmware/application/apps/ui_nrf_rx.hpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2017 Furrtek + * Copyright (C) 2020 Shao + * + * 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_NRF_RX_H__ +#define __UI_NRF_RX_H__ + +#include "ui.hpp" +#include "ui_navigation.hpp" +#include "ui_receiver.hpp" +#include "ui_record_view.hpp" // DEBUG + +#include "utility.hpp" + +namespace ui { + +class NRFRxView : public View { +public: + NRFRxView(NavigationView& nav); + ~NRFRxView(); + + void focus() override; + + std::string title() const override { return "NRF RX"; }; + +private: + void on_data(uint32_t value, bool is_data); + + uint8_t console_color { 0 }; + uint32_t prev_value { 0 }; + std::string str_log { "" }; + + RFAmpField field_rf_amp { + { 13 * 8, 0 * 16 } + }; + LNAGainField field_lna { + { 15 * 8, 0 * 16 } + }; + VGAGainField field_vga { + { 18 * 8, 0 * 16 } + }; + RSSI rssi { + { 21 * 8, 0, 6 * 8, 4 }, + }; + Channel channel { + { 21 * 8, 5, 6 * 8, 4 }, + }; + + FrequencyField field_frequency { + { 0 * 8, 0 * 16 }, + }; + + Text text_debug { + { 0 * 8, 1 * 16, 10 * 8, 16 }, + "DEBUG" + }; + + + Button button_modem_setup { + { 12 * 8, 1 * 16, 96, 24 }, + "Modem setup" + }; + + // DEBUG + RecordView record_view { + { 0 * 8, 3 * 16, 30 * 8, 1 * 16 }, + u"AFS_????", RecordView::FileType::WAV, 4096, 4 + }; + + Console console { + { 0, 4 * 16, 240, 240 } + }; + + void update_freq(rf::Frequency f); + //void on_data_afsk(const AFSKDataMessage& message); + + MessageHandlerRegistration message_handler_packet { + Message::ID::AFSKData, + [this](Message* const p) { + const auto message = static_cast(p); + this->on_data(message->value, message->is_data); + } + }; +}; + +} /* namespace ui */ + +#endif/*__UI_NRF_RX_H__*/ diff --git a/firmware/application/baseband_api.cpp b/firmware/application/baseband_api.cpp index beb95371..be969ac8 100644 --- a/firmware/application/baseband_api.cpp +++ b/firmware/application/baseband_api.cpp @@ -130,6 +130,16 @@ void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_ send_message(&message); } +void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) { + const NRFRxConfigureMessage message { + baudrate, + word_length, + trigger_value, + trigger_word + }; + 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 uint8_t symbol_count) { const AFSKTxConfigureMessage message { diff --git a/firmware/application/baseband_api.hpp b/firmware/application/baseband_api.hpp index 8562a90c..30ad6084 100644 --- a/firmware/application/baseband_api.hpp +++ b/firmware/application/baseband_api.hpp @@ -68,6 +68,7 @@ void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phas const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count); void kill_afsk(); void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word); +void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word); void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat, const uint32_t pause_symbols); void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift, diff --git a/firmware/application/bitmap.hpp b/firmware/application/bitmap.hpp index a4950deb..0d11ceab 100644 --- a/firmware/application/bitmap.hpp +++ b/firmware/application/bitmap.hpp @@ -1503,6 +1503,28 @@ static constexpr Bitmap bitmap_icon_replay { { 16, 16 }, bitmap_icon_replay_data }; +static constexpr uint8_t bitmap_icon_nrf_data[] = { + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x07, 0xE0, + 0x05, 0x20, + 0xCC, 0x33, + 0xF4, 0x2F, + 0xE6, 0x67, + 0xE2, 0x47, + 0x36, 0x5C, + 0x0E, 0xE0, + 0x06, 0x60, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, +}; +static constexpr Bitmap bitmap_icon_nrf { + { 16, 16 }, bitmap_icon_nrf_data +}; + static constexpr uint8_t bitmap_sig_sine_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 4c574c63..8e000fbf 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -34,6 +34,7 @@ #include "ui_adsb_rx.hpp" #include "ui_adsb_tx.hpp" #include "ui_afsk_rx.hpp" +#include "ui_nrf_rx.hpp" #include "ui_aprs_tx.hpp" #include "ui_bht_tx.hpp" #include "ui_coasterp.hpp" @@ -347,6 +348,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { { "ACARS", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push(); }, }, { "AIS Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.push(); } }, { "AFSK", ui::Color::yellow(), &bitmap_icon_receivers, [&nav](){ nav.push(); } }, + { "NRF", ui::Color::yellow(), &bitmap_icon_nrf, [&nav](){ nav.push(); } }, { "Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav](){ nav.push(); } }, { "ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push(); } }, { "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push(); } }, diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index fd70a03a..0d1000b8 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -311,6 +311,12 @@ set(MODE_CPPSRC proc_afskrx.cpp ) DeclareTargets(PAFR afskrx) +### NRF RX + +set(MODE_CPPSRC + proc_nrfrx.cpp +) +DeclareTargets(PNRR nrfrx) ### AIS diff --git a/firmware/baseband/proc_nrfrx.cpp b/firmware/baseband/proc_nrfrx.cpp new file mode 100644 index 00000000..277bc7f3 --- /dev/null +++ b/firmware/baseband/proc_nrfrx.cpp @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek + * Copyright (C) 2020 Shao + * + * 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 "proc_nrfrx.hpp" +#include "portapack_shared_memory.hpp" + +#include "event_m4.hpp" + +void NRFRxProcessor::execute(const buffer_c8_t& buffer) { + if (!configured) return; + + // FM demodulation + + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + feed_channel_stats(decim_0_out); + + auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer); + // Audio signal processing + for (size_t c = 0; c < audio_oversampled.count; c++) { + int result; + int g_srate = 4; //4 for 250KPS + //int g_srate = 1; //1 for 1MPS, not working yet + int32_t current_sample = audio_oversampled.p[c]; //if I directly use this, some results can pass crc but not correct. + rb_head++; + rb_head=(rb_head)%RB_SIZE; + + rb_buf[rb_head] = current_sample; + + skipSamples = skipSamples - 1; + + + if (skipSamples<1) + { + + + int32_t threshold_tmp=0; + for (int c=0;c<8*g_srate;c++) + { + threshold_tmp = threshold_tmp + (int32_t)rb_buf[(rb_head+c)%RB_SIZE]; + } + + g_threshold = (int32_t)threshold_tmp/(8*g_srate); + + int transitions=0; + if (rb_buf[(rb_head + 9*g_srate)%RB_SIZE] > g_threshold) + { + for (int c=0;c<8;c++) + { + if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] > rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE]) + transitions = transitions + 1; + } + } + else + { + for (int c=0;c<8;c++) + { + if (rb_buf[(rb_head + c*g_srate)%RB_SIZE] < rb_buf[(rb_head + (c+1)*g_srate)%RB_SIZE]) + transitions = transitions + 1; + } + } + + bool packet_detected=false; + //if ( transitions==4 && abs(g_threshold)<15500) + if ( transitions==4 && abs(g_threshold)<15500) + { + int packet_length = 0; + uint8_t tmp_buf[10]; + uint8_t packet_data[500]; + uint8_t packet_packed[50]; + uint16_t pcf; + uint32_t packet_crc; + uint32_t calced_crc; + uint64_t packet_addr_l; + + /* extract address */ + packet_addr_l=0; + + for (int t=0;t<5;t++) + { + bool current_bit; + uint8_t byte=0; + for (int c=0;c<8;c++) + { + if (rb_buf[(rb_head+(1*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7-c); + } + tmp_buf[t]=byte; + } + + for (int t=0;t<5;t++) packet_addr_l|=((uint64_t)tmp_buf[t])<<(4-t)*8; + + //channel_number = 26; + + + /* extract pcf */ + for (int t=0;t<2;t++) + { + bool current_bit; + uint8_t byte=0; + for (int c=0;c<8;c++) + { + if (rb_buf[(rb_head+(6*8+t*8+c)*g_srate)%RB_SIZE] > g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7-c); + } + tmp_buf[t]=byte; + } + + pcf = tmp_buf[0]<<8 | tmp_buf[1]; + pcf >>=7; + + /* extract packet length, avoid excessive length packets */ + if(packet_length == 0) + packet_length=(int)pcf>>3; + if (packet_length>32) + packet_detected = false; + + /* extract data */ + for (int t=0;t g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7-c); + } + packet_data[t]=byte; + } + + /* Prepare packed bit stream for CRC calculation */ + uint64_t packet_header=packet_addr_l; + packet_header<<=9; + packet_header|=pcf; + for (int c=0;c<7;c++){ + packet_packed[c]=(packet_header>>((6-c)*8))&0xFF; + } + + for (int c=0;c 0; i >>= 1) + { + bit = crc & 0x8000; + if (cc & i) + { + bit = !bit; + } + crc <<= 1; + if (bit) + { + crc ^= 0x1021; + } + } + crc &= 0xffff; + } + calced_crc = (uint16_t)(crc & 0xffff); + + /* extract crc */ + for (int t=0;t<2;t++) + { + bool current_bit; + uint8_t byte=0; + for (int c=0;c<8;c++) + { + if (rb_buf[(rb_head+((6+packet_length)*8+9+t*8+c)*g_srate)%RB_SIZE] > g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7-c); + } + tmp_buf[t]=byte; + } + packet_crc = tmp_buf[0]<<8 | tmp_buf[1]; + + /* NRF24L01+ packet found, dump information */ + //if (packet_addr_l==0xE7E7E7E7) + if (packet_crc==calced_crc) + { + data_message.is_data = false; + data_message.value = 'A'; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = packet_addr_l; + shared_memory.application_queue.push(data_message); + + for (int c=0;c<7;c++) + { + data_message.is_data = true; + data_message.value = packet_addr_l >> 8; + shared_memory.application_queue.push(data_message); + } + /*data_message.is_data = true; + data_message.value = packet_addr_l; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = packet_addr_l >> 8; + shared_memory.application_queue.push(data_message);*/ + + data_message.is_data = false; + data_message.value = 'B'; + shared_memory.application_queue.push(data_message); + + for (int c=0;cid == Message::ID::NRFRxConfigure) + configure(*reinterpret_cast(message)); +} + +void NRFRxProcessor::configure(const NRFRxConfigureMessage& message) { + decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432); + decim_1.configure(taps_200k_wfm_decim_1.taps, 131072); + demod.configure(audio_fs, 5000); + + configured = true; +} + +int main() { + EventDispatcher event_dispatcher { std::make_unique() }; + event_dispatcher.run(); + return 0; +} diff --git a/firmware/baseband/proc_nrfrx.hpp b/firmware/baseband/proc_nrfrx.hpp new file mode 100644 index 00000000..bfd9e348 --- /dev/null +++ b/firmware/baseband/proc_nrfrx.hpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek + * Copyright (C) 2020 Shao + * + * 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 __PROC_NRFRX_H__ +#define __PROC_NRFRX_H__ + +#include "baseband_processor.hpp" +#include "baseband_thread.hpp" +#include "rssi_thread.hpp" + +#include "dsp_decimate.hpp" +#include "dsp_demodulate.hpp" + +#include "audio_output.hpp" + +#include "fifo.hpp" +#include "message.hpp" + +class NRFRxProcessor : public BasebandProcessor { +public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + +private: + static constexpr size_t baseband_fs = 4000000; + static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2; + + BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; + RSSIThread rssi_thread { NORMALPRIO + 10 }; + + std::array dst { }; + const buffer_c16_t dst_buffer { + dst.data(), + dst.size() + }; + + std::array spectrum { }; + const buffer_c16_t spectrum_buffer { + spectrum.data(), + spectrum.size() + }; + + const buffer_s16_t work_audio_buffer { + (int16_t*)dst.data(), + sizeof(dst) / sizeof(int16_t) + }; + + + // Array size ok down to 375 bauds (24000 / 375) + std::array delay_line { 0 }; + std::array rb_buf { 0 }; + + /*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; + dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; + dsp::decimate::FIRAndDecimateComplex channel_filter { };*/ + dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { }; + dsp::decimate::FIRC16xR16x16Decim2 decim_1 { }; + + dsp::demodulate::FM demod { }; + int rb_head {-1}; + int32_t g_threshold {0}; + //uint8_t g_srate {8}; + uint8_t channel_number {38}; + int skipSamples {1000}; + int RB_SIZE {1000}; + + bool configured { false }; + + + void configure(const NRFRxConfigureMessage& message); + + AFSKDataMessage data_message { false, 0 }; +}; + +#endif/*__PROC_NRFRX_H__*/ diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index dfcc49ce..e1a507d1 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -78,6 +78,7 @@ public: AFSKRxConfigure = 22, StatusRefresh = 23, SamplerateConfig = 24, + NRFRxConfigure = 26, TXProgress = 30, Retune = 31, @@ -722,6 +723,27 @@ public: const bool trigger_word; }; + +class NRFRxConfigureMessage : public Message { +public: + constexpr NRFRxConfigureMessage( + const uint32_t baudrate, + const uint32_t word_length, + const uint32_t trigger_value, + const bool trigger_word + ) : Message { ID::NRFRxConfigure }, + baudrate(baudrate), + word_length(word_length), + trigger_value(trigger_value), + trigger_word(trigger_word) + { + } + + const uint32_t baudrate; + const uint32_t word_length; + const uint32_t trigger_value; + const bool trigger_word; +}; class PitchRSSIConfigureMessage : public Message { public: constexpr PitchRSSIConfigureMessage( diff --git a/firmware/common/spi_image.hpp b/firmware/common/spi_image.hpp index fd2c7b7d..ce2522c6 100644 --- a/firmware/common/spi_image.hpp +++ b/firmware/common/spi_image.hpp @@ -68,6 +68,7 @@ private: constexpr image_tag_t image_tag_acars { 'P', 'A', 'C', 'A' }; constexpr image_tag_t image_tag_adsb_rx { 'P', 'A', 'D', 'R' }; constexpr image_tag_t image_tag_afsk_rx { 'P', 'A', 'F', 'R' }; +constexpr image_tag_t image_tag_nrf_rx { 'P', 'N', 'R', 'R' }; constexpr image_tag_t image_tag_ais { 'P', 'A', 'I', 'S' }; constexpr image_tag_t image_tag_am_audio { 'P', 'A', 'M', 'A' }; constexpr image_tag_t image_tag_capture { 'P', 'C', 'A', 'P' }; From 40531e9230a82dd20851556f29678478e7d5754c Mon Sep 17 00:00:00 2001 From: Erwin Ried <1091420+eried@users.noreply.github.com> Date: Mon, 20 Apr 2020 06:50:03 +0200 Subject: [PATCH 2/3] Ble receiver (#337) * BLE app * Update ui_navigation.cpp Co-authored-by: Furrtek --- firmware/application/CMakeLists.txt | 1 + firmware/application/apps/ui_btle_rx.cpp | 160 +++++++++++ firmware/application/apps/ui_btle_rx.hpp | 107 +++++++ firmware/application/baseband_api.cpp | 12 +- firmware/application/baseband_api.hpp | 4 + firmware/application/bitmap.hpp | 22 ++ firmware/application/ui_navigation.cpp | 2 + firmware/baseband/CMakeLists.txt | 6 + firmware/baseband/proc_btlerx.cpp | 339 +++++++++++++++++++++++ firmware/baseband/proc_btlerx.hpp | 95 +++++++ firmware/common/message.hpp | 24 +- firmware/common/spi_image.hpp | 1 + 12 files changed, 770 insertions(+), 3 deletions(-) create mode 100644 firmware/application/apps/ui_btle_rx.cpp create mode 100644 firmware/application/apps/ui_btle_rx.hpp create mode 100644 firmware/baseband/proc_btlerx.cpp create mode 100644 firmware/baseband/proc_btlerx.hpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 851e021d..143f3266 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -217,6 +217,7 @@ set(CPPSRC apps/ui_adsb_rx.cpp apps/ui_adsb_tx.cpp apps/ui_afsk_rx.cpp + apps/ui_btle_rx.cpp apps/ui_nrf_rx.cpp apps/ui_aprs_tx.cpp apps/ui_bht_tx.cpp diff --git a/firmware/application/apps/ui_btle_rx.cpp b/firmware/application/apps/ui_btle_rx.cpp new file mode 100644 index 00000000..d4b9afee --- /dev/null +++ b/firmware/application/apps/ui_btle_rx.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2017 Furrtek + * Copyright (C) 2020 Shao + * + * 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_btle_rx.hpp" +#include "ui_modemsetup.hpp" + +#include "modems.hpp" +#include "audio.hpp" +#include "rtc_time.hpp" +#include "baseband_api.hpp" +#include "string_format.hpp" +#include "portapack_persistent_memory.hpp" + +using namespace portapack; +using namespace modems; + +namespace ui { + +void BTLERxView::focus() { + field_frequency.focus(); +} + +void BTLERxView::update_freq(rf::Frequency f) { + receiver_model.set_tuning_frequency(f); +} + +BTLERxView::BTLERxView(NavigationView& nav) { + baseband::run_image(portapack::spi_flash::image_tag_btle_rx); + + add_children({ + &rssi, + &channel, + &field_rf_amp, + &field_lna, + &field_vga, + &field_frequency, + &text_debug, + &button_modem_setup, + &record_view, + &console + }); + + // DEBUG + record_view.on_error = [&nav](std::string message) { + nav.display_modal("Error", message); + }; + record_view.set_sampling_rate(24000); + + // Auto-configure modem for LCR RX (will be removed later) + update_freq(2426000000); + auto def_bell202 = &modem_defs[0]; + persistent_memory::set_modem_baudrate(def_bell202->baudrate); + serial_format_t serial_format; + serial_format.data_bits = 7; + serial_format.parity = EVEN; + serial_format.stop_bits = 1; + serial_format.bit_order = LSB_FIRST; + persistent_memory::set_serial_format(serial_format); + + field_frequency.set_value(receiver_model.tuning_frequency()); + field_frequency.set_step(100); + field_frequency.on_change = [this](rf::Frequency f) { + update_freq(f); + }; + field_frequency.on_edit = [this, &nav]() { + auto new_view = nav.push(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + update_freq(f); + field_frequency.set_value(f); + }; + }; + + button_modem_setup.on_select = [&nav](Button&) { + nav.push(); + }; + + + // Auto-configure modem for LCR RX (will be removed later) + baseband::set_btle(persistent_memory::modem_baudrate(), 8, 0, false); + + audio::set_rate(audio::Rate::Hz_24000); + audio::output::start(); + + receiver_model.set_sampling_rate(4000000); + receiver_model.set_baseband_bandwidth(4000000); + receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio); + receiver_model.enable(); +} + +void BTLERxView::on_data(uint32_t value, bool is_data) { + //std::string str_console = "\x1B"; + std::string str_console = ""; + if (is_data) { + // Colorize differently after message splits + //str_console += (char)((console_color & 3) + 9); + + //value &= 0xFF; // ABCDEFGH + //value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); // EFGHABCD + //value = ((value & 0xCC) >> 2) | ((value & 0x33) << 2); // GHEFCDAB + //value = ((value & 0xAA) >> 1) | ((value & 0x55) << 1); // HGFEDCBA + //value &= 0x7F; // Ignore parity, which is the MSB now + + //if ((value >= 32) && (value < 127)) { + // str_console += (char)value; // Printable + //} + + //str_console += (char)'A'; + //str_console += (char)value; + //str_console += "[" + to_string_hex(value, 2) + "]"; + str_console += ":" + to_string_hex(value, 2) ; + console.write(str_console); + + + + /*if ((value != 0x7F) && (prev_value == 0x7F)) { + // Message split + console.writeln(""); + console_color++; + + + }*/ + //prev_value = value; + } else { + // Baudrate estimation + //text_debug.set("~" + to_string_dec_uint(value)); + if (value == 'A') + {console.write("mac");} + else if (value == 'B') + {console.writeln("");} + //console.writeln(""); + } +} + +BTLERxView::~BTLERxView() { + audio::output::stop(); + receiver_model.disable(); + baseband::shutdown(); +} + +} /* namespace ui */ diff --git a/firmware/application/apps/ui_btle_rx.hpp b/firmware/application/apps/ui_btle_rx.hpp new file mode 100644 index 00000000..9e331eef --- /dev/null +++ b/firmware/application/apps/ui_btle_rx.hpp @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2017 Furrtek + * Copyright (C) 2020 Shao + * + * 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_BTLE_RX_H__ +#define __UI_BTLE_RX_H__ + +#include "ui.hpp" +#include "ui_navigation.hpp" +#include "ui_receiver.hpp" +#include "ui_record_view.hpp" // DEBUG + +#include "utility.hpp" + +namespace ui { + +class BTLERxView : public View { +public: + BTLERxView(NavigationView& nav); + ~BTLERxView(); + + void focus() override; + + std::string title() const override { return "BTLE RX"; }; + +private: + void on_data(uint32_t value, bool is_data); + + uint8_t console_color { 0 }; + uint32_t prev_value { 0 }; + std::string str_log { "" }; + + RFAmpField field_rf_amp { + { 13 * 8, 0 * 16 } + }; + LNAGainField field_lna { + { 15 * 8, 0 * 16 } + }; + VGAGainField field_vga { + { 18 * 8, 0 * 16 } + }; + RSSI rssi { + { 21 * 8, 0, 6 * 8, 4 }, + }; + Channel channel { + { 21 * 8, 5, 6 * 8, 4 }, + }; + + FrequencyField field_frequency { + { 0 * 8, 0 * 16 }, + }; + + Text text_debug { + { 0 * 8, 1 * 16, 10 * 8, 16 }, + "DEBUG" + }; + + + Button button_modem_setup { + { 12 * 8, 1 * 16, 96, 24 }, + "Modem setup" + }; + + // DEBUG + RecordView record_view { + { 0 * 8, 3 * 16, 30 * 8, 1 * 16 }, + u"AFS_????", RecordView::FileType::WAV, 4096, 4 + }; + + Console console { + { 0, 4 * 16, 240, 240 } + }; + + void update_freq(rf::Frequency f); + //void on_data_afsk(const AFSKDataMessage& message); + + MessageHandlerRegistration message_handler_packet { + Message::ID::AFSKData, + [this](Message* const p) { + const auto message = static_cast(p); + this->on_data(message->value, message->is_data); + } + }; +}; + +} /* namespace ui */ + +#endif/*__UI_BTLE_RX_H__*/ diff --git a/firmware/application/baseband_api.cpp b/firmware/application/baseband_api.cpp index be969ac8..502047d2 100644 --- a/firmware/application/baseband_api.cpp +++ b/firmware/application/baseband_api.cpp @@ -130,6 +130,16 @@ void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_ send_message(&message); } +void set_btle(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) { + const BTLERxConfigureMessage message { + baudrate, + word_length, + trigger_value, + trigger_word + }; + send_message(&message); +} + void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word) { const NRFRxConfigureMessage message { baudrate, @@ -139,7 +149,7 @@ void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t }; 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 uint8_t symbol_count) { const AFSKTxConfigureMessage message { diff --git a/firmware/application/baseband_api.hpp b/firmware/application/baseband_api.hpp index 30ad6084..9592f8ca 100644 --- a/firmware/application/baseband_api.hpp +++ b/firmware/application/baseband_api.hpp @@ -68,7 +68,11 @@ void set_afsk_data(const uint32_t afsk_samples_per_bit, const uint32_t afsk_phas const uint8_t afsk_repeat, const uint32_t afsk_bw, const uint8_t symbol_count); void kill_afsk(); void set_afsk(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word); + +void set_btle(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word); + void set_nrf(const uint32_t baudrate, const uint32_t word_length, const uint32_t trigger_value, const bool trigger_word); + void set_ook_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint8_t repeat, const uint32_t pause_symbols); void set_fsk_data(const uint32_t stream_length, const uint32_t samples_per_bit, const uint32_t shift, diff --git a/firmware/application/bitmap.hpp b/firmware/application/bitmap.hpp index 0d11ceab..9626bede 100644 --- a/firmware/application/bitmap.hpp +++ b/firmware/application/bitmap.hpp @@ -1503,6 +1503,28 @@ static constexpr Bitmap bitmap_icon_replay { { 16, 16 }, bitmap_icon_replay_data }; +static constexpr uint8_t bitmap_icon_btle_data[] = { + 0x00, 0x00, + 0x80, 0x00, + 0x80, 0x01, + 0x80, 0x07, + 0x80, 0x0C, + 0x98, 0x06, + 0xF0, 0x03, + 0xE0, 0x01, + 0xE0, 0x01, + 0xF0, 0x03, + 0x98, 0x06, + 0x80, 0x0C, + 0x80, 0x07, + 0x80, 0x01, + 0x80, 0x00, + 0x00, 0x00, +}; +static constexpr Bitmap bitmap_icon_btle { + { 16, 16 }, bitmap_icon_btle_data +}; + static constexpr uint8_t bitmap_icon_nrf_data[] = { 0x00, 0x00, 0x00, 0x00, diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 8e000fbf..0d89d1c4 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -34,6 +34,7 @@ #include "ui_adsb_rx.hpp" #include "ui_adsb_tx.hpp" #include "ui_afsk_rx.hpp" +#include "ui_btle_rx.hpp" #include "ui_nrf_rx.hpp" #include "ui_aprs_tx.hpp" #include "ui_bht_tx.hpp" @@ -348,6 +349,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { { "ACARS", ui::Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push(); }, }, { "AIS Boats", ui::Color::green(), &bitmap_icon_ais, [&nav](){ nav.push(); } }, { "AFSK", ui::Color::yellow(), &bitmap_icon_receivers, [&nav](){ nav.push(); } }, + { "BTLE", ui::Color::yellow(), &bitmap_icon_btle, [&nav](){ nav.push(); } }, { "NRF", ui::Color::yellow(), &bitmap_icon_nrf, [&nav](){ nav.push(); } }, { "Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav](){ nav.push(); } }, { "ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push(); } }, diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index 0d1000b8..51f1bdb1 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -318,6 +318,12 @@ set(MODE_CPPSRC ) DeclareTargets(PNRR nrfrx) +### BTLE RX + +set(MODE_CPPSRC + proc_btlerx.cpp +) +DeclareTargets(PBTR btlerx) ### AIS set(MODE_CPPSRC diff --git a/firmware/baseband/proc_btlerx.cpp b/firmware/baseband/proc_btlerx.cpp new file mode 100644 index 00000000..f4e15571 --- /dev/null +++ b/firmware/baseband/proc_btlerx.cpp @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek + * Copyright (C) 2020 Shao + * + * 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 "proc_btlerx.hpp" +#include "portapack_shared_memory.hpp" + +#include "event_m4.hpp" + +void BTLERxProcessor::execute(const buffer_c8_t& buffer) { + if (!configured) return; + + // FM demodulation + + /*const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto channel = decim_1.execute(decim_0_out, dst_buffer); + + feed_channel_stats(channel); + + auto audio_oversampled = demod.execute(channel, work_audio_buffer);*/ + + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + feed_channel_stats(decim_0_out); + + auto audio_oversampled = demod.execute(decim_0_out, work_audio_buffer); + + /*std::fill(spectrum.begin(), spectrum.end(), 0); + for(size_t i=0; i g_threshold) + { + for (int c=0;c<8;c++) + { + if (rb_buf[(rb_head + c)%RB_SIZE] > rb_buf[(rb_head + c + 1)%RB_SIZE]) + transitions = transitions + 1; + } + } + else + { + for (int c=0;c<8;c++) + { + if (rb_buf[(rb_head + c)%RB_SIZE] < rb_buf[(rb_head + c + 1)%RB_SIZE]) + transitions = transitions + 1; + } + } + + bool packet_detected=false; + //if ( transitions==4 && abs(g_threshold)<15500) + if ( transitions==4) + { + + + uint8_t packet_data[500]; + int packet_length; + uint32_t packet_crc; + uint32_t calced_crc; + uint64_t packet_addr_l; + uint32_t result; + uint8_t crc[3]; + uint8_t packet_header_arr[2]; + + packet_addr_l=0; + for (int i=0;i<4;i++) + { + bool current_bit; + uint8_t byte=0; + for (int c=0;c<8;c++) + { + if (rb_buf[(rb_head + (i+1)*8 + c)%RB_SIZE] > g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7-c); + } + uint8_t byte_temp = (uint8_t) (((byte * 0x0802LU & 0x22110LU) | (byte * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); + packet_addr_l|=((uint64_t)byte_temp)<<(8*i); + } + + channel_number = 38; + + + for (int t=0;t<2;t++) + { + bool current_bit; + uint8_t byte=0; + for (int c=0;c<8;c++) + { + if (rb_buf[(rb_head + 5*8+t*8 + c)%RB_SIZE] > g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7-c); + } + + packet_header_arr[t] = byte; + } + + + + uint8_t byte_temp2 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); + uint8_t lfsr_1 = byte_temp2 | 2; + int header_length = 2; + int header_counter = 0; + while(header_length--) + { + for(uint8_t i = 0x80; i; i >>= 1) + { + if(lfsr_1 & 0x80) + { + lfsr_1 ^= 0x11; + (packet_header_arr[header_counter]) ^= i; + } + lfsr_1 <<= 1; + } + header_counter = header_counter + 1; + } + + + + if (packet_addr_l==0x8E89BED6) + { + + uint8_t byte_temp3 = (uint8_t) (((packet_header_arr[1] * 0x0802LU & 0x22110LU) | (packet_header_arr[1] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); + packet_length=byte_temp3&0x3F; + + } + else + { + packet_length=0; + } + + for (int t=0;t g_threshold) + current_bit = true; + else + current_bit = false; + byte |= current_bit << (7-c); + } + + packet_data[t] = byte; + } + + + + uint8_t byte_temp4 = (uint8_t) (((channel_number * 0x0802LU & 0x22110LU) | (channel_number * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); + uint8_t lfsr_2 = byte_temp4 | 2; + int pdu_crc_length = packet_length+2+3; + int pdu_crc_counter = 0; + while(pdu_crc_length--) + { + for(uint8_t i = 0x80; i; i >>= 1) + { + if(lfsr_2 & 0x80) + { + lfsr_2 ^= 0x11; + (packet_data[pdu_crc_counter]) ^= i; + } + lfsr_2 <<= 1; + } + pdu_crc_counter = pdu_crc_counter + 1; + } + + + + if (packet_addr_l==0x8E89BED6) + { + crc[0]=crc[1]=crc[2]=0x55; + } + else + { + crc[0]=crc[1]=crc[2]=0; + } + + uint8_t v, t, d, crc_length; + uint32_t crc_result=0; + crc_length = packet_length + 2; + int counter = 0; + while(crc_length--) + { + uint8_t byte_temp5 = (uint8_t) (((packet_data[counter] * 0x0802LU & 0x22110LU) | (packet_data[counter] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); + d = byte_temp5; + for(v = 0; v < 8; v++, d >>= 1) + { + t = crc[0] >> 7; + crc[0] <<= 1; + if(crc[1] & 0x80) crc[0] |= 1; + crc[1] <<= 1; + if(crc[2] & 0x80) crc[1] |= 1; + crc[2] <<= 1; + if(t != (d & 1)) + { + crc[2] ^= 0x5B; + crc[1] ^= 0x06; + } + } + counter = counter + 1; + } + for (v=0;v<3;v++) crc_result=(crc_result<<8)|crc[v]; + calced_crc = crc_result; + + packet_crc=0; + for (int c=0;c<3;c++) packet_crc=(packet_crc<<8)|packet_data[packet_length+2+c]; + + if (packet_addr_l==0x8E89BED6) + //if (packet_crc==calced_crc) + { + uint8_t mac_data[6]; + int counter = 0; + for (int i = 7; i >= 2; i--) + { + uint8_t byte_temp6 = (uint8_t) (((packet_data[i] * 0x0802LU & 0x22110LU) | (packet_data[i] * 0x8020LU & 0x88440LU)) * 0x10101LU >> 16); + //result = byte_temp6; + mac_data[counter] = byte_temp6; + counter = counter + 1; + } + + data_message.is_data = false; + data_message.value = 'A'; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = mac_data[0]; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = mac_data[1]; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = mac_data[2]; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = mac_data[3]; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = mac_data[4]; + shared_memory.application_queue.push(data_message); + + data_message.is_data = true; + data_message.value = mac_data[5]; + shared_memory.application_queue.push(data_message); + + data_message.is_data = false; + data_message.value = 'B'; + shared_memory.application_queue.push(data_message); + + packet_detected = true; + } + else + packet_detected = false; + } + + if (packet_detected) + { + skipSamples=20; + } + } + } +} + +void BTLERxProcessor::on_message(const Message* const message) { + if (message->id == Message::ID::BTLERxConfigure) + configure(*reinterpret_cast(message)); +} + +void BTLERxProcessor::configure(const BTLERxConfigureMessage& message) { + decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432); + decim_1.configure(taps_200k_wfm_decim_1.taps, 131072); + demod.configure(audio_fs, 5000); + + configured = true; +} + +int main() { + EventDispatcher event_dispatcher { std::make_unique() }; + event_dispatcher.run(); + return 0; +} diff --git a/firmware/baseband/proc_btlerx.hpp b/firmware/baseband/proc_btlerx.hpp new file mode 100644 index 00000000..2bb48872 --- /dev/null +++ b/firmware/baseband/proc_btlerx.hpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek + * Copyright (C) 2020 Shao + * + * 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 __PROC_BTLERX_H__ +#define __PROC_BTLERX_H__ + +#include "baseband_processor.hpp" +#include "baseband_thread.hpp" +#include "rssi_thread.hpp" + +#include "dsp_decimate.hpp" +#include "dsp_demodulate.hpp" + +#include "audio_output.hpp" + +#include "fifo.hpp" +#include "message.hpp" + +class BTLERxProcessor : public BasebandProcessor { +public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + +private: + static constexpr size_t baseband_fs = 4000000; + static constexpr size_t audio_fs = baseband_fs / 8 / 8 / 2; + + BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; + RSSIThread rssi_thread { NORMALPRIO + 10 }; + + std::array dst { }; + const buffer_c16_t dst_buffer { + dst.data(), + dst.size() + }; + + std::array spectrum { }; + const buffer_c16_t spectrum_buffer { + spectrum.data(), + spectrum.size() + }; + + const buffer_s16_t work_audio_buffer { + (int16_t*)dst.data(), + sizeof(dst) / sizeof(int16_t) + }; + + + // Array size ok down to 375 bauds (24000 / 375) + std::array delay_line { 0 }; + std::array rb_buf { 0 }; + + /*dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0 { }; + dsp::decimate::FIRC16xR16x32Decim8 decim_1 { }; + dsp::decimate::FIRAndDecimateComplex channel_filter { };*/ + dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0 { }; + dsp::decimate::FIRC16xR16x16Decim2 decim_1 { }; + + dsp::demodulate::FM demod { }; + int rb_head {-1}; + int32_t g_threshold {0}; + uint8_t channel_number {38}; + int skipSamples {1000}; + int RB_SIZE {1000}; + + bool configured { false }; + + + void configure(const BTLERxConfigureMessage& message); + + AFSKDataMessage data_message { false, 0 }; +}; + +#endif/*__PROC_BTLERX_H__*/ diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index e1a507d1..4ca93da9 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -78,6 +78,7 @@ public: AFSKRxConfigure = 22, StatusRefresh = 23, SamplerateConfig = 24, + BTLERxConfigure = 25, NRFRxConfigure = 26, TXProgress = 30, @@ -723,6 +724,25 @@ public: const bool trigger_word; }; +class BTLERxConfigureMessage : public Message { +public: + constexpr BTLERxConfigureMessage( + const uint32_t baudrate, + const uint32_t word_length, + const uint32_t trigger_value, + const bool trigger_word + ) : Message { ID::BTLERxConfigure }, + baudrate(baudrate), + word_length(word_length), + trigger_value(trigger_value), + trigger_word(trigger_word) + { + } + const uint32_t baudrate; + const uint32_t word_length; + const uint32_t trigger_value; + const bool trigger_word; +}; class NRFRxConfigureMessage : public Message { public: @@ -737,13 +757,13 @@ public: trigger_value(trigger_value), trigger_word(trigger_word) { - } - + } const uint32_t baudrate; const uint32_t word_length; const uint32_t trigger_value; const bool trigger_word; }; + class PitchRSSIConfigureMessage : public Message { public: constexpr PitchRSSIConfigureMessage( diff --git a/firmware/common/spi_image.hpp b/firmware/common/spi_image.hpp index ce2522c6..8c6ef60f 100644 --- a/firmware/common/spi_image.hpp +++ b/firmware/common/spi_image.hpp @@ -68,6 +68,7 @@ private: constexpr image_tag_t image_tag_acars { 'P', 'A', 'C', 'A' }; constexpr image_tag_t image_tag_adsb_rx { 'P', 'A', 'D', 'R' }; constexpr image_tag_t image_tag_afsk_rx { 'P', 'A', 'F', 'R' }; +constexpr image_tag_t image_tag_btle_rx { 'P', 'B', 'T', 'R' }; constexpr image_tag_t image_tag_nrf_rx { 'P', 'N', 'R', 'R' }; constexpr image_tag_t image_tag_ais { 'P', 'A', 'I', 'S' }; constexpr image_tag_t image_tag_am_audio { 'P', 'A', 'M', 'A' }; From e43f8148610a40df0d01864d7fa825759fa75c4c Mon Sep 17 00:00:00 2001 From: Erwin Ried <1091420+eried@users.noreply.github.com> Date: Mon, 20 Apr 2020 06:50:24 +0200 Subject: [PATCH 3/3] Analog tv app (#334) * Analog TV app (PAL) * Icon on main menu * Analog TV should be yellow Works for PAL only know, it would be nice to add NTSC in the future, or some customizable sync --- firmware/application/CMakeLists.txt | 2 + firmware/application/apps/analog_tv_app.cpp | 238 ++++++++++++++++++ firmware/application/apps/analog_tv_app.hpp | 133 ++++++++++ firmware/application/spectrum_color_lut.cpp | 259 ++++++++++++++++++++ firmware/application/spectrum_color_lut.hpp | 1 + firmware/application/ui/ui_tv.cpp | 251 +++++++++++++++++++ firmware/application/ui/ui_tv.hpp | 176 +++++++++++++ firmware/application/ui_navigation.cpp | 2 + firmware/baseband/CMakeLists.txt | 8 + firmware/baseband/proc_am_tv.cpp | 86 +++++++ firmware/baseband/proc_am_tv.hpp | 65 +++++ firmware/baseband/tv_collector.cpp | 122 +++++++++ firmware/baseband/tv_collector.hpp | 66 +++++ firmware/common/spi_image.hpp | 1 + 14 files changed, 1410 insertions(+) create mode 100644 firmware/application/apps/analog_tv_app.cpp create mode 100644 firmware/application/apps/analog_tv_app.hpp create mode 100644 firmware/application/ui/ui_tv.cpp create mode 100644 firmware/application/ui/ui_tv.hpp create mode 100644 firmware/baseband/proc_am_tv.cpp create mode 100644 firmware/baseband/proc_am_tv.hpp create mode 100644 firmware/baseband/tv_collector.cpp create mode 100644 firmware/baseband/tv_collector.hpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 143f3266..86b6f3fd 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -209,6 +209,7 @@ set(CPPSRC ui/ui_btngrid.cpp ui/ui_receiver.cpp ui/ui_rssi.cpp + ui/ui_tv.cpp ui/ui_spectrum.cpp ui/ui_tabview.cpp ui/ui_textentry.cpp @@ -253,6 +254,7 @@ set(CPPSRC apps/acars_app.cpp apps/ais_app.cpp apps/analog_audio_app.cpp + apps/analog_tv_app.cpp apps/capture_app.cpp apps/ert_app.cpp apps/lge_app.cpp diff --git a/firmware/application/apps/analog_tv_app.cpp b/firmware/application/apps/analog_tv_app.cpp new file mode 100644 index 00000000..8f498078 --- /dev/null +++ b/firmware/application/apps/analog_tv_app.cpp @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2018 Furrtek + * Copyright (C) 2020 Shao + * + * 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 "analog_tv_app.hpp" + +#include "baseband_api.hpp" + +#include "portapack.hpp" +#include "portapack_persistent_memory.hpp" +using namespace portapack; +using namespace tonekey; + +#include "audio.hpp" +#include "file.hpp" + +#include "utility.hpp" + +#include "string_format.hpp" + +namespace ui { + +/* AnalogTvView *******************************************************/ + +AnalogTvView::AnalogTvView( + NavigationView& nav +) : nav_ (nav) +{ + add_children({ + &rssi, + &channel, + &audio, + &field_frequency, + &field_lna, + &field_vga, + &options_modulation, + &field_volume, + &tv + }); + + field_frequency.set_value(receiver_model.tuning_frequency()); + field_frequency.set_step(receiver_model.frequency_step()); + 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(receiver_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + this->on_tuning_frequency_changed(f); + this->field_frequency.set_value(f); + }; + }; + + field_frequency.on_show_options = [this]() { + this->on_show_options_frequency(); + }; + + field_lna.on_show_options = [this]() { + this->on_show_options_rf_gain(); + }; + + field_vga.on_show_options = [this]() { + this->on_show_options_rf_gain(); + }; + + const auto modulation = receiver_model.modulation(); + options_modulation.set_by_value(toUType(ReceiverModel::Mode::WidebandFMAudio)); + options_modulation.on_change = [this](size_t, OptionsField::value_t v) { + this->on_modulation_changed(static_cast(v)); + }; + options_modulation.on_show_options = [this]() { + this->on_show_options_modulation(); + }; + + field_volume.set_value(0); + field_volume.on_change = [this](int32_t v) { + this->on_headphone_volume_changed(v); + }; + + tv.on_select = [this](int32_t offset) { + field_frequency.set_value(receiver_model.tuning_frequency() + offset); + }; + + update_modulation(static_cast(modulation)); + on_modulation_changed(ReceiverModel::Mode::WidebandFMAudio); +} + +AnalogTvView::~AnalogTvView() { + // TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do + // both? + audio::output::stop(); + + receiver_model.disable(); + + baseband::shutdown(); +} + +void AnalogTvView::on_hide() { + // TODO: Terrible kludge because widget system doesn't notify Waterfall that + // it's being shown or hidden. + tv.on_hide(); + View::on_hide(); +} + +void AnalogTvView::set_parent_rect(const Rect new_parent_rect) { + View::set_parent_rect(new_parent_rect); + + const ui::Rect tv_rect { 0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height }; + tv.set_parent_rect(tv_rect); +} + +void AnalogTvView::focus() { + field_frequency.focus(); +} + +void AnalogTvView::on_tuning_frequency_changed(rf::Frequency f) { + receiver_model.set_tuning_frequency(f); +} + +void AnalogTvView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) { + receiver_model.set_baseband_bandwidth(bandwidth_hz); +} + +void AnalogTvView::on_modulation_changed(const ReceiverModel::Mode modulation) { + // TODO: Terrible kludge because widget system doesn't notify Waterfall that + // it's being shown or hidden. + tv.on_hide(); + update_modulation(modulation); + on_show_options_modulation(); + tv.on_show(); +} + +void AnalogTvView::remove_options_widget() { + if( options_widget ) { + remove_child(options_widget.get()); + options_widget.reset(); + } + + field_lna.set_style(nullptr); + options_modulation.set_style(nullptr); + field_frequency.set_style(nullptr); +} + +void AnalogTvView::set_options_widget(std::unique_ptr new_widget) { + remove_options_widget(); + + if( new_widget ) { + options_widget = std::move(new_widget); + } else { + // TODO: Lame hack to hide options view due to my bad paint/damage algorithm. + options_widget = std::make_unique(options_view_rect, style_options_group_new.background); + } + add_child(options_widget.get()); +} + +void AnalogTvView::on_show_options_frequency() { + auto widget = std::make_unique(options_view_rect, &style_options_group_new); + + widget->set_step(receiver_model.frequency_step()); + widget->on_change_step = [this](rf::Frequency f) { + this->on_frequency_step_changed(f); + }; + widget->set_reference_ppm_correction(persistent_memory::correction_ppb() / 1000); + widget->on_change_reference_ppm_correction = [this](int32_t v) { + this->on_reference_ppm_correction_changed(v); + }; + + set_options_widget(std::move(widget)); + field_frequency.set_style(&style_options_group_new); +} + +void AnalogTvView::on_show_options_rf_gain() { + auto widget = std::make_unique(options_view_rect, &style_options_group_new); + + set_options_widget(std::move(widget)); + field_lna.set_style(&style_options_group_new); +} + +void AnalogTvView::on_show_options_modulation() { + std::unique_ptr widget; + + const auto modulation = static_cast(receiver_model.modulation()); + tv.show_audio_spectrum_view(true); + + set_options_widget(std::move(widget)); + options_modulation.set_style(&style_options_group_new); +} + +void AnalogTvView::on_frequency_step_changed(rf::Frequency f) { + receiver_model.set_frequency_step(f); + field_frequency.set_step(f); +} + +void AnalogTvView::on_reference_ppm_correction_changed(int32_t v) { + persistent_memory::set_correction_ppb(v * 1000); +} + +void AnalogTvView::on_headphone_volume_changed(int32_t v) { + //tv::TVView::set_headphone_volume(this,v); +} + +void AnalogTvView::update_modulation(const ReceiverModel::Mode modulation) { + audio::output::mute(); + + baseband::shutdown(); + + portapack::spi_flash::image_tag_t image_tag; + image_tag = portapack::spi_flash::image_tag_am_tv; + + baseband::run_image(image_tag); + + receiver_model.set_modulation(modulation); + receiver_model.set_sampling_rate(2000000); + receiver_model.set_baseband_bandwidth(2000000); + receiver_model.enable(); +} + +} /* namespace ui */ diff --git a/firmware/application/apps/analog_tv_app.hpp b/firmware/application/apps/analog_tv_app.hpp new file mode 100644 index 00000000..552f7f6b --- /dev/null +++ b/firmware/application/apps/analog_tv_app.hpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2018 Furrtek + * Copyright (C) 2020 Shao + * + * 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 __ANALOG_TV_APP_H__ +#define __ANALOG_TV_APP_H__ + +#include "receiver_model.hpp" + +#include "ui_receiver.hpp" +#include "ui_tv.hpp" +#include "ui_record_view.hpp" + +#include "ui_font_fixed_8x16.hpp" + +#include "tone_key.hpp" + +namespace ui { + +constexpr Style style_options_group_new { + .font = font::fixed_8x16, + .background = Color::blue(), + .foreground = Color::white(), +}; + +class AnalogTvView : public View { +public: + AnalogTvView(NavigationView& nav); + ~AnalogTvView(); + + void on_hide() override; + + void set_parent_rect(const Rect new_parent_rect) override; + + void focus() override; + + std::string title() const override { return "Analog TV"; }; + +private: + static constexpr ui::Dim header_height = 3 * 16; + + const Rect options_view_rect { 0 * 8, 1 * 16, 30 * 8, 1 * 16 }; + const Rect nbfm_view_rect { 0 * 8, 1 * 16, 18 * 8, 1 * 16 }; + + NavigationView& nav_; + + RSSI rssi { + { 21 * 8, 0, 6 * 8, 4 }, + }; + + Channel channel { + { 21 * 8, 5, 6 * 8, 4 }, + }; + + Audio audio { + { 21 * 8, 10, 6 * 8, 4 }, + }; + + FrequencyField field_frequency { + { 5 * 8, 0 * 16 }, + }; + + LNAGainField field_lna { + { 15 * 8, 0 * 16 } + }; + + VGAGainField field_vga { + { 18 * 8, 0 * 16 } + }; + + OptionsField options_modulation { + { 0 * 8, 0 * 16 }, + 4, + { + { "TV ", toUType(ReceiverModel::Mode::WidebandFMAudio) }, + { "TV ", toUType(ReceiverModel::Mode::WidebandFMAudio) }, + { "TV ", toUType(ReceiverModel::Mode::WidebandFMAudio) }, + } + }; + + NumberField field_volume { + { 27 * 8, 0 * 16 }, + 3, + { 0, 255 }, + 1, + ' ', + }; + + std::unique_ptr options_widget { }; + + tv::TVWidget tv { true }; + + void on_tuning_frequency_changed(rf::Frequency f); + void on_baseband_bandwidth_changed(uint32_t bandwidth_hz); + void on_modulation_changed(const ReceiverModel::Mode modulation); + void on_show_options_frequency(); + void on_show_options_rf_gain(); + void on_show_options_modulation(); + void on_frequency_step_changed(rf::Frequency f); + void on_reference_ppm_correction_changed(int32_t v); + void on_headphone_volume_changed(int32_t v); + void on_edit_frequency(); + + void remove_options_widget(); + void set_options_widget(std::unique_ptr new_widget); + + void update_modulation(const ReceiverModel::Mode modulation); + + +}; + +} /* namespace ui */ + +#endif/*__ANALOG_TV_APP_H__*/ diff --git a/firmware/application/spectrum_color_lut.cpp b/firmware/application/spectrum_color_lut.cpp index bd2379c2..e78a5322 100644 --- a/firmware/application/spectrum_color_lut.cpp +++ b/firmware/application/spectrum_color_lut.cpp @@ -538,3 +538,262 @@ const std::array spectrum_rgb3_lut { { { 252, 3, 0 }, { 255, 0, 0 }, } }; + +const std::array spectrum_rgb4_lut { { + { 0, 0, 0 }, + { 1, 1, 1 }, + { 2, 2, 2 }, + { 3, 3, 3 }, + { 4, 4, 4 }, + { 5, 5, 5 }, + { 6, 6, 6 }, + { 7, 7, 7 }, + { 8, 8, 8 }, + { 9, 9, 9 }, + { 10, 10, 10 }, + { 11, 11, 11 }, + { 12, 12, 12 }, + { 13, 13, 13 }, + { 14, 14, 14 }, + { 15, 15, 15 }, + { 16, 16, 16 }, + { 17, 17, 17 }, + { 18, 18, 18 }, + { 19, 19, 19 }, + { 20, 20, 20 }, + { 21, 21, 21 }, + { 22, 22, 22 }, + { 23, 23, 23 }, + { 24, 24, 24 }, + { 25, 25, 25 }, + { 26, 26, 26 }, + { 27, 27, 27 }, + { 28, 28, 28 }, + { 29, 29, 29 }, + { 30, 30, 30 }, + { 31, 31, 31 }, + { 32, 32, 32 }, + { 33, 33, 33 }, + { 34, 34, 34 }, + { 35, 35, 35 }, + { 36, 36, 36 }, + { 37, 37, 37 }, + { 38, 38, 38 }, + { 39, 39, 39 }, + { 40, 40, 40 }, + { 41, 41, 41 }, + { 42, 42, 42 }, + { 43, 43, 43 }, + { 44, 44, 44 }, + { 45, 45, 45 }, + { 46, 46, 46 }, + { 47, 47, 47 }, + { 48, 48, 48 }, + { 49, 49, 49 }, + { 50, 50, 50 }, + { 51, 51, 51 }, + { 52, 52, 52 }, + { 53, 53, 53 }, + { 54, 54, 54 }, + { 55, 55, 55 }, + { 56, 56, 56 }, + { 57, 57, 57 }, + { 58, 58, 58 }, + { 59, 59, 59 }, + { 60, 60, 60 }, + { 61, 61, 61 }, + { 62, 62, 62 }, + { 63, 63, 63 }, + { 64, 64, 64 }, + { 65, 65, 65 }, + { 66, 66, 66 }, + { 67, 67, 67 }, + { 68, 68, 68 }, + { 69, 69, 69 }, + { 70, 70, 70 }, + { 71, 71, 71 }, + { 72, 72, 72 }, + { 73, 73, 73 }, + { 74, 74, 74 }, + { 75, 75, 75 }, + { 76, 76, 76 }, + { 77, 77, 77 }, + { 78, 78, 78 }, + { 79, 79, 79 }, + { 80, 80, 80 }, + { 81, 81, 81 }, + { 82, 82, 82 }, + { 83, 83, 83 }, + { 84, 84, 84 }, + { 85, 85, 85 }, + { 86, 86, 86 }, + { 87, 87, 87 }, + { 88, 88, 88 }, + { 89, 89, 89 }, + { 90, 90, 90 }, + { 91, 91, 91 }, + { 92, 92, 92 }, + { 93, 93, 93 }, + { 94, 94, 94 }, + { 95, 95, 95 }, + { 96, 96, 96 }, + { 97, 97, 97 }, + { 98, 98, 98 }, + { 99, 99, 99 }, + { 100, 100, 100 }, + { 101, 101, 101 }, + { 102, 102, 102 }, + { 103, 103, 103 }, + { 104, 104, 104 }, + { 105, 105, 105 }, + { 106, 106, 106 }, + { 107, 107, 107 }, + { 108, 108, 108 }, + { 109, 109, 109 }, + { 110, 110, 110 }, + { 111, 111, 111 }, + { 112, 112, 112 }, + { 113, 113, 113 }, + { 114, 114, 114 }, + { 115, 115, 115 }, + { 116, 116, 116 }, + { 117, 117, 117 }, + { 118, 118, 118 }, + { 119, 119, 119 }, + { 120, 120, 120 }, + { 121, 121, 121 }, + { 122, 122, 122 }, + { 123, 123, 123 }, + { 124, 124, 124 }, + { 125, 125, 125 }, + { 126, 126, 126 }, + { 127, 127, 127 }, + { 128, 128, 128 }, + { 129, 129, 129 }, + { 130, 130, 130 }, + { 131, 131, 131 }, + { 132, 132, 132 }, + { 133, 133, 133 }, + { 134, 134, 134 }, + { 135, 135, 135 }, + { 136, 136, 136 }, + { 137, 137, 137 }, + { 138, 138, 138 }, + { 139, 139, 139 }, + { 140, 140, 140 }, + { 141, 141, 141 }, + { 142, 142, 142 }, + { 143, 143, 143 }, + { 144, 144, 144 }, + { 145, 145, 145 }, + { 146, 146, 146 }, + { 147, 147, 147 }, + { 148, 148, 148 }, + { 149, 149, 149 }, + { 150, 150, 150 }, + { 151, 151, 151 }, + { 152, 152, 152 }, + { 153, 153, 153 }, + { 154, 154, 154 }, + { 155, 155, 155 }, + { 156, 156, 156 }, + { 157, 157, 157 }, + { 158, 158, 158 }, + { 159, 159, 159 }, + { 160, 160, 160 }, + { 161, 161, 161 }, + { 162, 162, 162 }, + { 163, 163, 163 }, + { 164, 164, 164 }, + { 165, 165, 165 }, + { 166, 166, 166 }, + { 167, 167, 167 }, + { 168, 168, 168 }, + { 169, 169, 169 }, + { 170, 170, 170 }, + { 171, 171, 171 }, + { 172, 172, 172 }, + { 173, 173, 173 }, + { 174, 174, 174 }, + { 175, 175, 175 }, + { 176, 176, 176 }, + { 177, 177, 177 }, + { 178, 178, 178 }, + { 179, 179, 179 }, + { 180, 180, 180 }, + { 181, 181, 181 }, + { 182, 182, 182 }, + { 183, 183, 183 }, + { 184, 184, 184 }, + { 185, 185, 185 }, + { 186, 186, 186 }, + { 187, 187, 187 }, + { 188, 188, 188 }, + { 189, 189, 189 }, + { 190, 190, 190 }, + { 191, 191, 191 }, + { 192, 192, 192 }, + { 193, 193, 193 }, + { 194, 194, 194 }, + { 195, 195, 195 }, + { 196, 196, 196 }, + { 197, 197, 197 }, + { 198, 198, 198 }, + { 199, 199, 199 }, + { 200, 200, 200 }, + { 201, 201, 201 }, + { 202, 202, 202 }, + { 203, 203, 203 }, + { 204, 204, 204 }, + { 205, 205, 205 }, + { 206, 206, 206 }, + { 207, 207, 207 }, + { 208, 208, 208 }, + { 209, 209, 209 }, + { 210, 210, 210 }, + { 211, 211, 211 }, + { 212, 212, 212 }, + { 213, 213, 213 }, + { 214, 214, 214 }, + { 215, 215, 215 }, + { 216, 216, 216 }, + { 217, 217, 217 }, + { 218, 218, 218 }, + { 219, 219, 219 }, + { 220, 220, 220 }, + { 221, 221, 221 }, + { 222, 222, 222 }, + { 223, 223, 223 }, + { 224, 224, 224 }, + { 225, 225, 225 }, + { 226, 226, 226 }, + { 227, 227, 227 }, + { 228, 228, 228 }, + { 229, 229, 229 }, + { 230, 230, 230 }, + { 231, 231, 231 }, + { 232, 232, 232 }, + { 233, 233, 233 }, + { 234, 234, 234 }, + { 235, 235, 235 }, + { 236, 236, 236 }, + { 237, 237, 237 }, + { 238, 238, 238 }, + { 239, 239, 239 }, + { 240, 240, 240 }, + { 241, 241, 241 }, + { 242, 242, 242 }, + { 243, 243, 243 }, + { 244, 244, 244 }, + { 245, 245, 245 }, + { 246, 246, 246 }, + { 247, 247, 247 }, + { 248, 248, 248 }, + { 249, 249, 249 }, + { 250, 250, 250 }, + { 251, 251, 251 }, + { 252, 252, 252 }, + { 253, 253, 253 }, + { 254, 254, 254 }, + { 255, 255, 255 }, +} }; diff --git a/firmware/application/spectrum_color_lut.hpp b/firmware/application/spectrum_color_lut.hpp index afec033e..74c554a8 100644 --- a/firmware/application/spectrum_color_lut.hpp +++ b/firmware/application/spectrum_color_lut.hpp @@ -28,5 +28,6 @@ extern const std::array spectrum_rgb2_lut; extern const std::array spectrum_rgb3_lut; +extern const std::array spectrum_rgb4_lut; #endif/*__SPECTRUM_COLOR_LUT_H__*/ diff --git a/firmware/application/ui/ui_tv.cpp b/firmware/application/ui/ui_tv.cpp new file mode 100644 index 00000000..59c99e07 --- /dev/null +++ b/firmware/application/ui/ui_tv.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2020 Shao + * + * 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_tv.hpp" + +#include "spectrum_color_lut.hpp" + +#include "portapack.hpp" +using namespace portapack; + +#include "baseband_api.hpp" + +#include "string_format.hpp" + +#include +#include + +namespace ui { +namespace tv { + +/* TimeScopeView******************************************************/ + +TimeScopeView::TimeScopeView( + const Rect parent_rect +) : View { parent_rect } +{ + set_focusable(true); + + add_children({ + //&labels, + //&field_frequency, + &waveform + }); + + /*field_frequency.on_change = [this](int32_t) { + set_dirty(); + }; + field_frequency.set_value(10);*/ +} + +void TimeScopeView::paint(Painter& painter) { + const auto r = screen_rect(); + + painter.fill_rectangle(r, Color::black()); + + // Cursor + /* + const Rect r_cursor { + field_frequency.value() / (48000 / 240), r.bottom() - 32 - cursor_band_height, + 1, cursor_band_height + }; + painter.fill_rectangle( + r_cursor, + Color::red() + );*/ +} + +void TimeScopeView::on_audio_spectrum(const AudioSpectrum* spectrum) { + for (size_t i = 0; i < spectrum->db.size(); i++) + audio_spectrum[i] = ((int16_t)spectrum->db[i] - 127) * 256; + waveform.set_dirty(); +} + +/* TVView *********************************************************/ + +void TVView::on_show() { + clear(); + + const auto screen_r = screen_rect(); + display.scroll_set_area(screen_r.top(), screen_r.bottom()); +} + +void TVView::on_hide() { + /* TODO: Clear region to eliminate brief flash of content at un-shifted + * position? + */ + display.scroll_disable(); +} + +void TVView::paint(Painter& painter) { + // Do nothing. + (void)painter; +} + +void TVView::on_adjust_xcorr(uint8_t xcorr){ + x_correction = xcorr; +} + +void TVView::on_channel_spectrum( + const ChannelSpectrum& spectrum +) { + //portapack has limitations + // 1.screen resolution (less than 240x320) 2.samples each call back (128 or 256) + // 3.memory size (for ui::Color, the buffer size + //spectrum.db[i] is 256 long + //768x625 ->128x625 ->128x312 -> 128x104 + //originally @6MHz sample rate, the PAL should be 768x625 + //I reduced sample rate to 2MHz(3 times less samples), then calculate mag (effectively decimate by 2) + //the resolution is now changed to 128x625. The total decimation factor is 6, which changes how many samples in a line + //However 625 is too large for the screen, also interlaced scanning is harder to realize in portapack than normal computer. + //So I decided to simply drop half of the lines, once y is larger than 625/2=312.5 or 312, I recognize it as a new frame. + //then the resolution is changed to 128x312 + //128x312 is now able to put into a 240x320 screen, but the buffer for a whole frame is 128x312=39936, which is too large + //according to my test, I can only make a buffer with a length of 13312 of type ui::Color. which is 1/3 of what I wanted. + //So now the resolution is changed to 128x104, the height is shrinked to 1/3 of the original height. + //I was expecting to see 1/3 height of original video. + + //Look how nice is that! I am now able to meet the requirements of 1 and 3 for portapack. Also the length of a line is 128 + //Each call back gives me 256 samples which is exactly 2 lines. What a coincidence! + + //After some experiment, I did some improvements. + //1.I found that instead of 1/3 of the frame is shown, I got 3 whole frames shrinked into one window. + //So I made the height twice simply by painting 2 identical lines in the place of original lines + //2.I found sometimes there is an horizontal offset, so I added x_correction to move the frame back to center manually + //3.I changed video_buffer's type, from ui::Color to uint_8, since I don't need 3 digit to represent a grey scale value. + //I was hoping that by doing this, I can have a longer buffer like 39936, then the frame will looks better vertically + //however this is useless until now. + + for(size_t i=0; i<256; i++) + { + //video_buffer[i+count*256] = spectrum_rgb4_lut[spectrum.db[i]]; + video_buffer_int[i+count*256] = 255 - spectrum.db[i]; + } + count = count + 1; + if (count == 52 -1) + { + ui::Color line_buffer[128]; + Coord line; + uint32_t bmp_px; + + /*for (line = 0; line < 104; line++) + { + for (bmp_px = 0; bmp_px < 128; bmp_px++) + { + //line_buffer[bmp_px] = video_buffer[bmp_px+line*128]; + line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line*128 + x_correction]]; + } + + display.render_line({ 0, line + 100 }, 128, line_buffer); + }*/ + for (line = 0; line < 208; line=line+2) + { + for (bmp_px = 0; bmp_px < 128; bmp_px++) + { + //line_buffer[bmp_px] = video_buffer[bmp_px+line*128]; + line_buffer[bmp_px] = spectrum_rgb4_lut[video_buffer_int[bmp_px+line/2*128 + x_correction]]; + } + + display.render_line({ 0, line + 100 }, 128, line_buffer); + display.render_line({ 0, line + 101 }, 128, line_buffer); + } + count = 0; + } + +} + +void TVView::clear() { + display.fill_rectangle( + screen_rect(), + Color::black() + ); +} + +/* TVWidget *******************************************************/ + +TVWidget::TVWidget(const bool cursor) { + add_children({ + &tv_view, + &field_xcorr + }); + field_xcorr.set_value(10); +} + +void TVWidget::on_show() { + baseband::spectrum_streaming_start(); +} + +void TVWidget::on_hide() { + baseband::spectrum_streaming_stop(); +} + +void TVWidget::show_audio_spectrum_view(const bool show) { + if ((audio_spectrum_view && show) || (!audio_spectrum_view && !show)) return; + + if (show) { + audio_spectrum_view = std::make_unique(audio_spectrum_view_rect); + add_child(audio_spectrum_view.get()); + update_widgets_rect(); + } else { + audio_spectrum_update = false; + remove_child(audio_spectrum_view.get()); + audio_spectrum_view.reset(); + update_widgets_rect(); + } +} + +void TVWidget::update_widgets_rect() { + if (audio_spectrum_view) { + tv_view.set_parent_rect(tv_reduced_rect); + } else { + tv_view.set_parent_rect(tv_normal_rect); + } + tv_view.on_show(); +} + +void TVWidget::set_parent_rect(const Rect new_parent_rect) { + View::set_parent_rect(new_parent_rect); + + tv_normal_rect = { 0, scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height}; + tv_reduced_rect = { 0, audio_spectrum_height + scale_height, new_parent_rect.width(), new_parent_rect.height() - scale_height - audio_spectrum_height }; + + update_widgets_rect(); +} + +void TVWidget::paint(Painter& painter) { + // TODO: + (void)painter; +} + +void TVWidget::on_channel_spectrum(const ChannelSpectrum& spectrum) { + tv_view.on_channel_spectrum(spectrum); + tv_view.on_adjust_xcorr(field_xcorr.value()); + sampling_rate = spectrum.sampling_rate; + +} + +void TVWidget::on_audio_spectrum() { + audio_spectrum_view->on_audio_spectrum(audio_spectrum_data); +} + +} /* namespace tv */ +} /* namespace ui */ diff --git a/firmware/application/ui/ui_tv.hpp b/firmware/application/ui/ui_tv.hpp new file mode 100644 index 00000000..1fb7887a --- /dev/null +++ b/firmware/application/ui/ui_tv.hpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2020 Shao + * + * 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_TV_H__ +#define __UI_TV_H__ + +#include "ui.hpp" +#include "ui_widget.hpp" + +#include "event_m0.hpp" + +#include "message.hpp" + +#include +#include + +namespace ui { +namespace tv { + +class TimeScopeView : public View { +public: + TimeScopeView(const Rect parent_rect); + + void paint(Painter& painter) override; + + void on_audio_spectrum(const AudioSpectrum* spectrum); + +private: + static constexpr int cursor_band_height = 4; + + int16_t audio_spectrum[128] { 0 }; + + /*Labels labels { + { { 6 * 8, 0 * 16 }, "Hz", Color::light_grey() } + };*/ + /* + NumberField field_frequency { + { 0 * 8, 0 * 16 }, + 5, + { 0, 48000 }, + 48000 / 240, + ' ' + };*/ + + Waveform waveform { + { 0, 1 * 16 + cursor_band_height, 30 * 8, 2 * 16 }, + audio_spectrum, + 128, + 0, + false, + Color::white() + }; +}; + +class TVView : public Widget { +public: + void on_show() override; + void on_hide() override; + + void paint(Painter& painter) override; + void on_channel_spectrum(const ChannelSpectrum& spectrum); + void on_adjust_xcorr(uint8_t xcorr); + //ui::Color video_buffer[13312]; + uint8_t video_buffer_int[13312+128] { 0 }; //128 is for the over length caused by x_correction + uint32_t count=0; + uint8_t x_correction=0; +private: + void clear(); + +}; + +class TVWidget : public View { +public: + std::function on_select { }; + + TVWidget(const bool cursor = false); + + TVWidget(const TVWidget&) = delete; + TVWidget(TVWidget&&) = delete; + TVWidget& operator=(const TVWidget&) = delete; + TVWidget& operator=(TVWidget&&) = delete; + + void on_show() override; + void on_hide() override; + + void set_parent_rect(const Rect new_parent_rect) override; + + void show_audio_spectrum_view(const bool show); + + void paint(Painter& painter) override; + NumberField field_xcorr { + { 0 * 8, 0 * 16 }, + 5, + { 0, 128 }, + 1, + ' ' + }; + +private: + void update_widgets_rect(); + + const Rect audio_spectrum_view_rect { 0 * 8, 0 * 16, 30 * 8, 2 * 16 + 20 }; + static constexpr Dim audio_spectrum_height = 16 * 2 + 20; + static constexpr Dim scale_height = 20; + + TVView tv_view { }; + + ChannelSpectrumFIFO* channel_fifo { nullptr }; + AudioSpectrum* audio_spectrum_data { nullptr }; + bool audio_spectrum_update { false }; + + std::unique_ptr audio_spectrum_view { }; + + int sampling_rate { 0 }; + int32_t cursor_position { 0 }; + ui::Rect tv_normal_rect { }; + ui::Rect tv_reduced_rect { }; + + MessageHandlerRegistration message_handler_channel_spectrum_config { + Message::ID::ChannelSpectrumConfig, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->channel_fifo = message.fifo; + } + }; + MessageHandlerRegistration message_handler_audio_spectrum { + Message::ID::AudioSpectrum, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->audio_spectrum_data = message.data; + this->audio_spectrum_update = true; + } + }; + MessageHandlerRegistration message_handler_frame_sync { + Message::ID::DisplayFrameSync, + [this](const Message* const) { + if( this->channel_fifo ) { + ChannelSpectrum channel_spectrum; + while( channel_fifo->out(channel_spectrum) ) { + this->on_channel_spectrum(channel_spectrum); + } + } + if (this->audio_spectrum_update) { + this->audio_spectrum_update = false; + this->on_audio_spectrum(); + } + } + }; + + void on_channel_spectrum(const ChannelSpectrum& spectrum); + void on_audio_spectrum(); +}; + +} /* namespace tv */ +} /* namespace ui */ + +#endif/*__UI_TV_H__*/ diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 0d89d1c4..bd1eb0aa 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -70,6 +70,7 @@ #include "acars_app.hpp" #include "ais_app.hpp" #include "analog_audio_app.hpp" +#include "analog_tv_app.hpp" #include "capture_app.hpp" #include "ert_app.hpp" #include "lge_app.hpp" @@ -352,6 +353,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { { "BTLE", ui::Color::yellow(), &bitmap_icon_btle, [&nav](){ nav.push(); } }, { "NRF", ui::Color::yellow(), &bitmap_icon_nrf, [&nav](){ nav.push(); } }, { "Audio", ui::Color::green(), &bitmap_icon_speaker, [&nav](){ nav.push(); } }, + { "Analog TV", ui::Color::yellow(), &bitmap_icon_sstv, [&nav](){ nav.push(); } }, { "ERT Meter", ui::Color::green(), &bitmap_icon_ert, [&nav](){ nav.push(); } }, { "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push(); } }, { "Radiosnde", ui::Color::yellow(), &bitmap_icon_sonde, [&nav](){ nav.push(); } }, diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index 51f1bdb1..028e62e4 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -116,6 +116,7 @@ set(CPPSRC dsp_goertzel.cpp matched_filter.cpp spectrum_collector.cpp + tv_collector.cpp stream_input.cpp stream_output.cpp dsp_squelch.cpp @@ -338,6 +339,13 @@ set(MODE_CPPSRC ) DeclareTargets(PAMA am_audio) +### AM TV + +set(MODE_CPPSRC + proc_am_tv.cpp +) +DeclareTargets(PAMT am_tv) + ### Audio transmit set(MODE_CPPSRC diff --git a/firmware/baseband/proc_am_tv.cpp b/firmware/baseband/proc_am_tv.cpp new file mode 100644 index 00000000..4c2c7e9d --- /dev/null +++ b/firmware/baseband/proc_am_tv.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek + * Copyright (C) 2020 Shao + * + * 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 "proc_am_tv.hpp" + +#include "portapack_shared_memory.hpp" +#include "dsp_fft.hpp" +#include "event_m4.hpp" + +#include + +void WidebandFMAudio::execute(const buffer_c8_t& buffer) { + if( !configured ) { + return; + } + + std::fill(spectrum.begin(), spectrum.end(), 0); + + for(size_t i=0; iid) { + case Message::ID::UpdateSpectrum: + case Message::ID::SpectrumStreamingConfig: + channel_spectrum.on_message(message); + break; + + case Message::ID::WFMConfigure: + configure(*reinterpret_cast(message)); + break; + + default: + break; + } +} + +void WidebandFMAudio::configure(const WFMConfigureMessage& message) { + configured = true; +} + + +int main() { + EventDispatcher event_dispatcher { std::make_unique() }; + event_dispatcher.run(); + return 0; +} diff --git a/firmware/baseband/proc_am_tv.hpp b/firmware/baseband/proc_am_tv.hpp new file mode 100644 index 00000000..356877b0 --- /dev/null +++ b/firmware/baseband/proc_am_tv.hpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2016 Furrtek + * Copyright (C) 2020 Shao + * + * 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 __PROC_AM_TV_H__ +#define __PROC_AM_TV_H__ + +#include "baseband_processor.hpp" +#include "baseband_thread.hpp" +#include "rssi_thread.hpp" + +#include "dsp_types.hpp" +#include "dsp_decimate.hpp" +#include "dsp_demodulate.hpp" +#include "block_decimator.hpp" + +#include "tv_collector.hpp" + +class WidebandFMAudio : public BasebandProcessor { +public: + void execute(const buffer_c8_t& buffer) override; + + void on_message(const Message* const message) override; + +private: + static constexpr size_t baseband_fs = 2000000; + + BasebandThread baseband_thread { baseband_fs, this, NORMALPRIO + 20, baseband::Direction::Receive }; + RSSIThread rssi_thread { NORMALPRIO + 10 }; + + std::array dst { }; + const buffer_c16_t dst_buffer { + dst.data(), + dst.size() + }; + + AudioSpectrum audio_spectrum { }; + TvCollector channel_spectrum { }; + std::array spectrum { }; + + bool configured { false }; + void configure(const WFMConfigureMessage& message); + +}; + +#endif/*__PROC_AM_TV_H__*/ diff --git a/firmware/baseband/tv_collector.cpp b/firmware/baseband/tv_collector.cpp new file mode 100644 index 00000000..a18eaf46 --- /dev/null +++ b/firmware/baseband/tv_collector.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * Copyright (C) 2020 Shao + * + * 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 "tv_collector.hpp" + +#include "dsp_fft.hpp" + +#include "utility.hpp" +#include "event_m4.hpp" +#include "portapack_shared_memory.hpp" + +#include "event_m4.hpp" + +#include + +void TvCollector::on_message(const Message* const message) { + switch(message->id) { + case Message::ID::UpdateSpectrum: + update(); + break; + + case Message::ID::SpectrumStreamingConfig: + set_state(*reinterpret_cast(message)); + break; + + default: + break; + } +} + +void TvCollector::set_state(const SpectrumStreamingConfigMessage& message) { + if( message.mode == SpectrumStreamingConfigMessage::Mode::Running ) { + start(); + } else { + stop(); + } +} + +void TvCollector::start() { + streaming = true; + ChannelSpectrumConfigMessage message { &fifo }; + shared_memory.application_queue.push(message); +} + +void TvCollector::stop() { + streaming = false; + fifo.reset_in(); +} + +void TvCollector::set_decimation_factor( + const size_t decimation_factor +) { + channel_spectrum_decimator.set_factor(decimation_factor); +} + +/* TODO: Refactor to register task with idle thread? + * It's sad that the idle thread has to call all the way back here just to + * perform the deferred task on the buffer of data we prepared. + */ + +void TvCollector::feed( + const buffer_c16_t& channel +) { + // Called from baseband processing thread. + channel_spectrum_decimator.feed(channel,[this](const buffer_c16_t& data) {this->post_message(data);}); +} + +void TvCollector::post_message(const buffer_c16_t& data) { + // Called from baseband processing thread. + float re, im; + float mag; + float max; + if( streaming && !channel_spectrum_request_update ) { + for(size_t i=0; i<256; i++) + { + const auto s = data.p[i]; + re = (float)(data.p[i].real()); + im = (float)(data.p[i].imag()); + mag = __builtin_sqrtf((re * re) + (im * im)) ; + channel_spectrum[i] = {mag, mag}; + } + channel_spectrum_sampling_rate = data.sampling_rate; + channel_spectrum_request_update = true; + EventDispatcher::events_flag(EVT_MASK_SPECTRUM); + } +} + + +void TvCollector::update() { + // Called from idle thread (after EVT_MASK_SPECTRUM is flagged) + if( streaming && channel_spectrum_request_update ) { + ChannelSpectrum spectrum; + spectrum.sampling_rate = channel_spectrum_sampling_rate; + for(size_t i=0; i +#include + +#include "message.hpp" + +class TvCollector { +public: + void on_message(const Message* const message); + + void set_decimation_factor(const size_t decimation_factor); + + void feed( + const buffer_c16_t& channel + ); + +private: + BlockDecimator channel_spectrum_decimator { 1 }; + ChannelSpectrum fifo_data[1 << ChannelSpectrumConfigMessage::fifo_k] { }; + ChannelSpectrumFIFO fifo { fifo_data, ChannelSpectrumConfigMessage::fifo_k }; + + volatile bool channel_spectrum_request_update { false }; + bool streaming { false }; + std::array, 256> channel_spectrum { }; + uint32_t channel_spectrum_sampling_rate { 0 }; + + void post_message(const buffer_c16_t& data); + + void set_state(const SpectrumStreamingConfigMessage& message); + void start(); + void stop(); + + void update(); + +}; + +#endif/*__TV_COLLECTOR_H__*/ diff --git a/firmware/common/spi_image.hpp b/firmware/common/spi_image.hpp index 8c6ef60f..023c1317 100644 --- a/firmware/common/spi_image.hpp +++ b/firmware/common/spi_image.hpp @@ -72,6 +72,7 @@ constexpr image_tag_t image_tag_btle_rx { 'P', 'B', 'T', 'R' }; constexpr image_tag_t image_tag_nrf_rx { 'P', 'N', 'R', 'R' }; constexpr image_tag_t image_tag_ais { 'P', 'A', 'I', 'S' }; constexpr image_tag_t image_tag_am_audio { 'P', 'A', 'M', 'A' }; +constexpr image_tag_t image_tag_am_tv { 'P', 'A', 'M', 'T' }; constexpr image_tag_t image_tag_capture { 'P', 'C', 'A', 'P' }; constexpr image_tag_t image_tag_ert { 'P', 'E', 'R', 'T' }; constexpr image_tag_t image_tag_nfm_audio { 'P', 'N', 'F', 'M' };