diff --git a/firmware/application/Makefile b/firmware/application/Makefile index 9dcb4d34..0ad8b72c 100755 --- a/firmware/application/Makefile +++ b/firmware/application/Makefile @@ -161,6 +161,7 @@ CPPSRC = main.cpp \ ui_setup.cpp \ ui_debug.cpp \ ui_rds.cpp \ + ui_lcr.cpp \ ui_console.cpp \ ui_receiver.cpp \ ui_spectrum.cpp \ diff --git a/firmware/application/transmitter_model.cpp b/firmware/application/transmitter_model.cpp new file mode 100644 index 00000000..35801b66 --- /dev/null +++ b/firmware/application/transmitter_model.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. + * + * 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 "transmitter_model.hpp" + +#include "portapack_shared_memory.hpp" +#include "portapack_persistent_memory.hpp" +#include "portapack.hpp" +using namespace portapack; + +rf::Frequency TransmitterModel::tuning_frequency() const { + return persistent_memory::tuned_frequency(); +} + +void TransmitterModel::set_tuning_frequency(rf::Frequency f) { + persistent_memory::set_tuned_frequency(f); + update_tuning_frequency(); +} + +bool TransmitterModel::rf_amp() const { + return rf_amp_; +} + +void TransmitterModel::set_rf_amp(bool enabled) { + rf_amp_ = enabled; + update_rf_amp(); +} + +int32_t TransmitterModel::lna() const { + return lna_gain_db_; +} + +void TransmitterModel::set_lna(int32_t v_db) { + lna_gain_db_ = v_db; + update_lna(); +} + +uint32_t TransmitterModel::baseband_bandwidth() const { + return baseband_bandwidth_; +} + +void TransmitterModel::set_baseband_bandwidth(uint32_t v) { + baseband_bandwidth_ = v; + update_baseband_bandwidth(); +} + +int32_t TransmitterModel::vga() const { + return vga_gain_db_; +} + +void TransmitterModel::set_vga(int32_t v_db) { + vga_gain_db_ = v_db; + update_vga(); +} + +uint32_t TransmitterModel::sampling_rate() const { + return baseband_configuration.sampling_rate; +} + +void TransmitterModel::set_sampling_rate(uint32_t hz) { + baseband_configuration.sampling_rate = hz; + update_baseband_configuration(); +} + +uint32_t TransmitterModel::modulation() const { + return baseband_configuration.mode; +} + +void TransmitterModel::set_modulation(int32_t v) { + baseband_configuration.mode = v; + update_modulation(); +} + +uint32_t TransmitterModel::baseband_oversampling() const { + // TODO: Rename decimation_factor. + return baseband_configuration.decimation_factor; +} + +void TransmitterModel::set_baseband_oversampling(uint32_t v) { + baseband_configuration.decimation_factor = v; + update_baseband_configuration(); +} + +void TransmitterModel::enable() { + radio::set_direction(rf::Direction::Transmit); + update_tuning_frequency(); + update_rf_amp(); + update_lna(); + update_vga(); + update_baseband_bandwidth(); + update_baseband_configuration(); + radio::streaming_enable(); +} + +void TransmitterModel::disable() { + /* TODO: This is a dumb hack to stop baseband from working so hard. */ + BasebandConfigurationMessage message { + .configuration = { + .mode = -1, + .sampling_rate = 0, + .decimation_factor = 1, + } + }; + shared_memory.baseband_queue.push(&message); + while( !message.is_free() ); + + radio::disable(); +} + +int32_t TransmitterModel::tuning_offset() { + return -(sampling_rate() / 4); +} + +void TransmitterModel::update_tuning_frequency() { + radio::set_tuning_frequency(tuning_frequency()); +} + +void TransmitterModel::update_rf_amp() { + radio::set_rf_amp(rf_amp_); +} + +void TransmitterModel::update_lna() { + radio::set_lna_gain(lna_gain_db_); +} + +void TransmitterModel::update_baseband_bandwidth() { + radio::set_baseband_filter_bandwidth(baseband_bandwidth_); +} + +void TransmitterModel::update_vga() { + radio::set_vga_gain(vga_gain_db_); +} + +void TransmitterModel::update_modulation() { + update_baseband_configuration(); +} + +void TransmitterModel::update_baseband_configuration() { + clock_manager.set_sampling_frequency(sampling_rate() * baseband_oversampling()); + update_tuning_frequency(); + radio::set_baseband_decimation_by(baseband_oversampling()); + + BasebandConfigurationMessage message { baseband_configuration }; + shared_memory.baseband_queue.push(&message); + + // Block until message is consumed, since we allocated it on the stack. + while( !message.is_free() ); +} diff --git a/firmware/application/transmitter_model.hpp b/firmware/application/transmitter_model.hpp new file mode 100644 index 00000000..2fa29631 --- /dev/null +++ b/firmware/application/transmitter_model.hpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * + * 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 __TRANSMITTER_MODEL_H__ +#define __TRANSMITTER_MODEL_H__ + +#include +#include + +#include "clock_manager.hpp" +#include "message.hpp" +#include "rf_path.hpp" +#include "max2837.hpp" +#include "volume.hpp" + +class TransmitterModel { +public: + constexpr TransmitterModel( + ClockManager& clock_manager + ) : clock_manager(clock_manager) + { + } + + rf::Frequency tuning_frequency() const; + void set_tuning_frequency(rf::Frequency f); + + bool rf_amp() const; + void set_rf_amp(bool enabled); + + int32_t lna() const; + void set_lna(int32_t v_db); + + uint32_t baseband_bandwidth() const; + void set_baseband_bandwidth(uint32_t v); + + int32_t vga() const; + void set_vga(int32_t v_db); + + uint32_t sampling_rate() const; + void set_sampling_rate(uint32_t hz); + + uint32_t modulation() const; + void set_modulation(int32_t v); + + uint32_t baseband_oversampling() const; + void set_baseband_oversampling(uint32_t v); + + void enable(); + void disable(); + +private: + bool rf_amp_ { true }; + int32_t lna_gain_db_ { 0 }; + uint32_t baseband_bandwidth_ { max2837::filter::bandwidth_minimum }; + int32_t vga_gain_db_ { 8 }; + BasebandConfiguration baseband_configuration { + .mode = 15, + .sampling_rate = 2280000, + .decimation_factor = 1, + }; + ClockManager& clock_manager; + + int32_t tuning_offset(); + + void update_tuning_frequency(); + void update_rf_amp(); + void update_lna(); + void update_baseband_bandwidth(); + void update_vga(); + void update_modulation(); + void update_baseband_configuration(); +}; + +#endif/*__TRANSMITTER_MODEL_H__*/ diff --git a/firmware/application/ui_lcr.cpp b/firmware/application/ui_lcr.cpp new file mode 100644 index 00000000..09ae9858 --- /dev/null +++ b/firmware/application/ui_lcr.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * + * 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_rds.hpp" +#include "ui_lcr.hpp" +#include "ui_receiver.hpp" + +#include "ch.h" + +#include "ff.h" +#include "hackrf_gpio.hpp" +#include "portapack.hpp" +#include "radio.hpp" + +#include "hackrf_hal.hpp" +#include "portapack_shared_memory.hpp" + +#include +#include + +using namespace hackrf::one; + +namespace ui { + +void LCRView::focus() { + button_setam_a.focus(); +} + +LCRView::~LCRView() { + transmitter_model.disable(); +} + +char hexify(char in) { + if (in > 9) in += 7; + return in + 0x30; +} + +void LCRView::paint(Painter& painter) { + char eom[3] = { 3, 0, 0 }; + uint8_t checksum = 0, i; + char teststr[16]; + + Point offset = { + static_cast(120), + static_cast(32) + }; + + painter.draw_string( + screen_pos() + offset, + style(), + rgsb + ); + + offset.y += 40; + + painter.draw_string( + screen_pos() + offset, + style(), + litteral[0] + ); + + offset.y += 40; + + painter.draw_string( + screen_pos() + offset, + style(), + litteral[1] + ); + + offset.y += 40; + + painter.draw_string( + screen_pos() + offset, + style(), + litteral[2] + ); + + offset.y += 40; + + painter.draw_string( + screen_pos() + offset, + style(), + litteral[3] + ); + + for (i = 0; i < 4; i++) { + while (strlen(litteral[i]) < 7) { + strcat(litteral[i], " "); + } + } + + // Recreate LCR frame + memset(lcrframe, 0, 256); + lcrframe[0] = 127; + lcrframe[1] = 127; + lcrframe[2] = 127; + lcrframe[3] = 127; + lcrframe[4] = 127; + lcrframe[5] = 15; + strcat(lcrframe, rgsb); + strcat(lcrframe, "PA AM=1 AF=\""); + strcat(lcrframe, litteral[0]); + strcat(lcrframe, "\" CL=0 AM=2 AF=\""); + strcat(lcrframe, litteral[1]); + strcat(lcrframe, "\" CL=0 AM=3 AF=\""); + strcat(lcrframe, litteral[2]); + strcat(lcrframe, "\" CL=0 AM=4 AF=\""); + strcat(lcrframe, litteral[3]); + strcat(lcrframe, "\" CL=0 EC=A SAB=0"); + + //Checksum + i = 5; + while (lcrframe[i]) { + checksum ^= lcrframe[i]; + i++; + } + checksum ^= 3; + checksum &= 0x7F; + eom[1] = checksum; + + strcat(lcrframe, eom); + + teststr[0] = hexify(eom[1] >> 4); + teststr[1] = hexify(eom[1] & 15); + teststr[2] = 0; + offset.x = 220; + painter.draw_string( + screen_pos() + offset, + style(), + teststr + ); +} + +void LCRView::updfreq(rf::Frequency f) { + char finalstr[9] = {0}; + transmitter_model.set_tuning_frequency(f); + + auto mhz = to_string_dec_int(f / 1000000, 3); + auto hz100 = to_string_dec_int((f / 100) % 10000, 4, '0'); + + strcat(finalstr, mhz.c_str()); + strcat(finalstr, "."); + strcat(finalstr, hz100.c_str()); + + this->button_setfreq.set_text(finalstr); +} + +//TODO: 7 char pad for litterals + +LCRView::LCRView( + NavigationView& nav, + TransmitterModel& transmitter_model +) : transmitter_model(transmitter_model) +{ + transmitter_model.set_modulation(16); + transmitter_model.set_tuning_frequency(f); + memset(litteral, 0, 4*8); + memset(rgsb, 0, 5); + + rgsb[0] = 'E'; + rgsb[1] = 'b'; + rgsb[2] = 'G'; + rgsb[3] = '0'; // Predef. + + add_children({ { + &button_setrgsb, + &button_setam_a, + &button_setam_b, + &button_setam_c, + &button_setam_d, + &button_setfreq, + &button_setbaud, + &button_transmit, + &button_exit + } }); + + button_setrgsb.on_select = [this,&nav](Button&){ + auto an_view = new AlphanumView { nav, rgsb, 4 }; + nav.push(an_view); + }; + button_setfreq.on_select = [this,&nav](Button&){ + auto new_view = new FrequencyKeypadView { nav, this->transmitter_model.tuning_frequency() }; + new_view->on_changed = [this](rf::Frequency f) { + updfreq(f); + }; + nav.push(new_view); + }; + + button_setam_a.on_select = [this,&nav](Button&){ + auto an_view = new AlphanumView { nav, litteral[0], 7 }; + nav.push(an_view); + }; + button_setam_b.on_select = [this,&nav](Button&){ + auto an_view = new AlphanumView { nav, litteral[1], 7 }; + nav.push(an_view); + }; + button_setam_c.on_select = [this,&nav](Button&){ + auto an_view = new AlphanumView { nav, litteral[2], 7 }; + nav.push(an_view); + }; + button_setam_d.on_select = [this,&nav](Button&){ + auto an_view = new AlphanumView { nav, litteral[3], 7 }; + nav.push(an_view); + }; + button_setbaud.on_select = [this](Button&){ + if (baudrate == 1200) { + baudrate = 2400; + button_setbaud.set_text("2400 bps"); + } else { + baudrate = 1200; + button_setbaud.set_text("1200 bps"); + } + }; + + button_transmit.on_select = [this,&transmitter_model](Button&){ + uint16_t c; + if (baudrate == 1200) + shared_memory.fskspb = 190; + else + shared_memory.fskspb = 95; + + for (c = 0; c < 256; c++) { + shared_memory.lcrdata[c] = this->lcrframe[c]; + } + transmitter_model.enable(); + }; + + button_exit.on_select = [&nav](Button&){ + nav.pop(); + }; +} + +} /* namespace ui */ diff --git a/firmware/application/ui_lcr.hpp b/firmware/application/ui_lcr.hpp new file mode 100644 index 00000000..fd05f291 --- /dev/null +++ b/firmware/application/ui_lcr.hpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui.hpp" +#include "ui_widget.hpp" +#include "ui_painter.hpp" +#include "ui_menu.hpp" +#include "ui_navigation.hpp" +#include "ui_font_fixed_8x16.hpp" +#include "clock_manager.hpp" +#include "message.hpp" +#include "rf_path.hpp" +#include "max2837.hpp" +#include "volume.hpp" +#include "transmitter_model.hpp" + +namespace ui { + +class LCRView : public View { +public: + LCRView(NavigationView& nav, TransmitterModel& transmitter_model); + ~LCRView(); + + void updfreq(rf::Frequency f); + void focus() override; + void paint(Painter& painter) override; + +private: + char litteral[4][8]; + char rgsb[5]; + char lcrframe[256]; + uint16_t baudrate = 1200; + rf::Frequency f = 160000000; + TransmitterModel& transmitter_model; + + Button button_setrgsb { + { 16, 24, 96, 32 }, + "Set RGSB" + }; + + Button button_setam_a { + { 16, 64, 96, 32 }, + "AM 1" + }; + + Button button_setam_b { + { 16, 64+40, 96, 32 }, + "AM 2" + }; + + Button button_setam_c { + { 16, 64+40+40, 96, 32 }, + "AM 3" + }; + + Button button_setam_d { + { 16, 64+40+40+40, 96, 32 }, + "AM 4" + }; + + Button button_setfreq { + { 4, 232, 96, 32 }, + "160.0000" + }; + Button button_setbaud { + { 4, 270, 96, 32 }, + "1200bps" + }; + + Button button_transmit { + { 120, 232, 96, 32 }, + "Transmit" + }; + Button button_exit { + { 120, 270, 96, 32 }, + "Exit" + }; +}; + +} /* namespace ui */ diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 086f539d..aecf7eca 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -28,6 +28,7 @@ #include "ui_debug.hpp" #include "ui_receiver.hpp" #include "ui_rds.hpp" +#include "ui_lcr.hpp" #include "portapack.hpp" #include "m4_startup.hpp" @@ -97,11 +98,12 @@ void NavigationView::focus() { /* SystemMenuView ********************************************************/ SystemMenuView::SystemMenuView(NavigationView& nav) { - add_items<8>({ { + add_items<9>({ { { "Receiver", [&nav](){ nav.push(new ReceiverView { nav, receiver_model }); } }, { "Capture", [&nav](){ nav.push(new NotImplementedView { nav }); } }, { "Analyze", [&nav](){ nav.push(new NotImplementedView { nav }); } }, - { "RDS toolbox",[&nav](){ nav.push(new RDSView { nav, transmitter_model }); } }, + { "RDS TX", [&nav](){ nav.push(new RDSView { nav, transmitter_model }); } }, + { "LCR TX", [&nav](){ nav.push(new LCRView { nav, transmitter_model }); } }, { "Setup", [&nav](){ nav.push(new SetupMenuView { nav }); } }, { "About", [&nav](){ nav.push(new AboutView { nav }); } }, { "Debug", [&nav](){ nav.push(new DebugMenuView { nav }); } }, @@ -160,6 +162,8 @@ HackRFFirmwareView::HackRFFirmwareView(NavigationView& nav) { __WFE(); } }; + + //377.6M: bouts de musique button_no.on_select = [&nav](Button&){ nav.pop(); diff --git a/firmware/application/ui_rds.cpp b/firmware/application/ui_rds.cpp new file mode 100644 index 00000000..fecd341f --- /dev/null +++ b/firmware/application/ui_rds.cpp @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * + * 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_rds.hpp" + +#include "ch.h" + +#include "ff.h" +#include "hackrf_gpio.hpp" +#include "portapack.hpp" +#include "radio.hpp" + +#include "hackrf_hal.hpp" +#include "portapack_shared_memory.hpp" + +#include + +using namespace hackrf::one; + +namespace ui { + +AlphanumView::AlphanumView( + NavigationView& nav, + char txt[], + uint8_t max_len +) { + _max_len = max_len; + _lowercase = false; + + static constexpr Style style_alpha { + .font = font::fixed_8x16, + .background = Color::red(), + .foreground = Color::black(), + }; + + static constexpr Style style_num { + .font = font::fixed_8x16, + .background = Color::yellow(), + .foreground = Color::black(), + }; + + txtidx = 0; + memcpy(txtinput, txt, max_len+1); + + add_child(&text_input); + + const auto button_fn = [this](Button& button) { + this->on_button(button); + }; + + const char* const key_caps = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ. !<"; + + size_t n = 0; + for(auto& button : buttons) { + add_child(&button); + const std::string label { + key_caps[n] + }; + button.on_select = button_fn; + button.set_parent_rect({ + static_cast((n % 5) * button_w), + static_cast((n / 5) * button_h + 18), + button_w, button_h + }); + button.set_text(label); + if ((n < 10) || (n == 39)) + button.set_style(&style_num); + else + button.set_style(&style_alpha); + n++; + } + + add_child(&button_lowercase); + button_lowercase.on_select = [this, &nav, txt, max_len](Button&) { + if (_lowercase == true) { + _lowercase = false; + button_lowercase.set_text("LC"); + } else { + _lowercase = true; + button_lowercase.set_text("UC"); + } + }; + + add_child(&button_done); + button_done.on_select = [this, &nav, txt, max_len](Button&) { + memcpy(txt, txtinput, max_len+1); + nav.pop(); + }; + + update_text(); +} + +void AlphanumView::focus() { + button_done.focus(); +} + +char * AlphanumView::value() { + return txtinput; +} + +void AlphanumView::on_button(Button& button) { + const auto s = button.text(); + if( s == "<" ) { + char_delete(); + } else { + if (_lowercase == true) + char_add(s[0] + 32); + else + char_add(s[0]); + } + update_text(); +} + +void AlphanumView::char_add(const char c) { + if (txtidx < _max_len) { + txtinput[txtidx] = c; + txtidx++; + } +} + +void AlphanumView::char_delete() { + if (txtidx) { + txtidx--; + txtinput[txtidx] = ' '; + } +} + +void AlphanumView::update_text() { + text_input.set(txtinput); +} + +void RDSView::focus() { + button_setpsn.focus(); +} + +RDSView::~RDSView() { + transmitter_model.disable(); +} + +std::string to_string_bin(const uint32_t n, const uint8_t l) { + char p[32]; + for (uint8_t c = 0; c < l; c++) { + if ((n<>15) ^ (CRC>>9); + if (doinv) CRC ^= 0b0011011100; + CRC = ((CRC<<1) | doinv) & 0x3FF; + } + + return (blockdata<<10) | (CRC ^ offset); +} + +//Make PI +//Set frequency +//TA/TP flags +//Group selection +//RST SNCF +//Jammer +//Microphone troll +//CTCSS + +void RDSView::paint(Painter& painter) { + uint8_t c; + + uint32_t group[4][4] = { + {0b1111100001001001, //PI + 0b0000110011101000, //Address + 0b1111100001001001, //PI + 0b0000000000000000}, //Replaced + {0b1111100001001001, //PI + 0b0000110011101001, //Address + 0b1111100001001001, //PI + 0b0000000000000000}, //Replaced + {0b1111100001001001, //PI + 0b0000110011101010, //Address + 0b1111100001001001, //PI + 0b0000000000000000}, //Replaced + {0b1111100001001001, //PI + 0b0000110011101011, //Address + 0b1111100001001001, //PI + 0b0000000000000000}, //Replaced + }; + + //Insert PSN data in groups + group[0][3] = (psname[0]<<8) | psname[1]; + group[1][3] = (psname[2]<<8) | psname[3]; + group[2][3] = (psname[4]<<8) | psname[5]; + group[3][3] = (psname[6]<<8) | psname[7]; + + //Generate checkbits + for (c = 0; c < 4; c++) { + group[c][0] = makeblock(group[c][0], 0b0011111100); + group[c][1] = makeblock(group[c][1], 0b0110011000); + group[c][2] = makeblock(group[c][2], 0b1101010000); //C' + group[c][3] = makeblock(group[c][3], 0b0110110100); + } + + const Point offset = { + static_cast(64), + static_cast(32) + }; + + const auto text = psname; + painter.draw_string( + screen_pos() + offset, + style(), + text + ); + + for (c = 0; c < 16; c++) { + shared_memory.rdsdata[c] = group[c >> 2][c & 3]; + } +} + +RDSView::RDSView( + NavigationView& nav, + TransmitterModel& transmitter_model +) : transmitter_model(transmitter_model) +{ + transmitter_model.set_tuning_frequency(93000000); + strcpy(psname, "TEST1234"); + + add_children({ { + &text_title, + &button_setpsn, + &button_transmit, + &button_exit + } }); + + button_setpsn.on_select = [this,&nav](Button&){ + auto an_view = new AlphanumView { nav, psname, 8 }; + nav.push(an_view); + }; + + button_transmit.on_select = [&transmitter_model](Button&){ + transmitter_model.enable(); + }; + + button_exit.on_select = [&nav](Button&){ + nav.pop(); + }; +} + +} /* namespace ui */ diff --git a/firmware/application/ui_rds.hpp b/firmware/application/ui_rds.hpp new file mode 100644 index 00000000..2f777d20 --- /dev/null +++ b/firmware/application/ui_rds.hpp @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui.hpp" +#include "ui_widget.hpp" +#include "ui_painter.hpp" +#include "ui_menu.hpp" +#include "ui_navigation.hpp" +#include "ui_font_fixed_8x16.hpp" +#include "clock_manager.hpp" +#include "message.hpp" +#include "rf_path.hpp" +#include "max2837.hpp" +#include "volume.hpp" +#include "transmitter_model.hpp" + +namespace ui { + +class AlphanumView : public View { +public: + //std::function on_changed; + + AlphanumView(NavigationView& nav, char txt[], uint8_t max_len); + + void focus() override; + + char * value(); + + uint8_t txtidx; + + //void set_value(char * new_txtinput); + void char_add(const char c); + void char_delete(); + + //rf::Frequency value() const; + //void set_value(const rf::Frequency new_value); + +private: + uint8_t _max_len; + bool _lowercase; + static constexpr size_t button_w = 240 / 5; + static constexpr size_t button_h = 28; + char txtinput[9]; + + Text text_input { + { 88, 0, 240, 16 } + }; + + std::array buttons; + + Button button_lowercase { + { 88+64+16, 270, 32, 24 }, + "LC" + }; + + Button button_done { + { 88, 270, 64, 24 }, + "Done" + }; + + void on_button(Button& button); + + void update_text(); +}; + +class RDSView : public View { +public: + RDSView(NavigationView& nav, TransmitterModel& transmitter_model); + ~RDSView(); + + void focus() override; + void paint(Painter& painter) override; + +private: + char psname[9]; + TransmitterModel& transmitter_model; + + Text text_title { + { 76, 16, 88, 16 }, + "RDS toolbox" + }; + + Button button_setpsn { + { 72, 92, 96, 32 }, + "Set PSN" + }; + + Button button_transmit { + { 72, 130, 96, 32 }, + "Transmit" + }; + + Button button_exit { + { 72, 270, 96, 32 }, + "Exit" + }; +}; + +} /* namespace ui */ diff --git a/firmware/baseband/main.cpp b/firmware/baseband/main.cpp index c34df643..1ad86f06 100755 --- a/firmware/baseband/main.cpp +++ b/firmware/baseband/main.cpp @@ -760,6 +760,65 @@ private: int32_t k; }; +class LCRFSKProcessor : public BasebandProcessor { +public: + void execute(buffer_c8_t buffer) override { + + for (size_t i = 0; i= 9) { + s = 0; + fsk_samples_per_bit = shared_memory.fskspb; + if(sample_count >= fsk_samples_per_bit) { + cur_byte = shared_memory.lcrdata[bit_pos / 8]; + if (!cur_byte) { + //TransmitterModel::disable(); + bit_pos = 0; + cur_byte = shared_memory.lcrdata[0]; + } + cur_bit = cur_byte >> (7-(bit_pos % 8)) & 1; + + bit_pos++; + sample_count = 0; + } + sample_count++; + + if (cur_bit) + aphase += 267187; + else + aphase += 489844; + + sample = sintab[(aphase & 0x03FF0000)>>16]; + } else { + s++; + } + + //FM + frq = sample * 967; + + phase = (phase + frq); + sphase = phase + (256<<16); + + re = sintab[(sphase & 0x03FF0000)>>16]; + im = sintab[(phase & 0x03FF0000)>>16]; + + buffer.p[i] = {(int8_t)re,(int8_t)im}; + } + } + +private: + uint32_t fsk_samples_per_bit; + int8_t re, im; + uint8_t s; + uint32_t bit_pos; + uint8_t cur_byte = 0; + uint8_t cur_bit = 0; + uint32_t sample_count; + uint32_t aphase, phase, sphase; + int32_t sample, sig, frq; +}; + static BasebandProcessor* baseband_processor { nullptr }; static BasebandConfiguration baseband_configuration; @@ -1024,6 +1083,11 @@ int main(void) { direction = baseband::Direction::Transmit; baseband_processor = new RDSProcessor(); break; + + case 16: + direction = baseband::Direction::Transmit; + baseband_processor = new LCRFSKProcessor(); + break; default: break; diff --git a/firmware/common/portapack_shared_memory.hpp b/firmware/common/portapack_shared_memory.hpp index f222e5cd..ab19ba13 100644 --- a/firmware/common/portapack_shared_memory.hpp +++ b/firmware/common/portapack_shared_memory.hpp @@ -42,6 +42,8 @@ struct SharedMemory { int test; uint32_t rdsdata[16]; + char lcrdata[256]; + uint32_t fskspb; }; extern SharedMemory& shared_memory;