2021-03-07 16:05:23 -06:00
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_aprs_rx.hpp"
#include "audio.hpp"
#include "rtc_time.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
2024-03-25 02:44:49 -05:00
#include "file_path.hpp"
2021-03-07 16:05:23 -06:00
using namespace portapack ;
void APRSLogger :: log_raw_data ( const std :: string & data ) {
2023-05-19 13:39:35 -07:00
log_file . write_entry ( data );
2021-03-07 16:05:23 -06:00
}
namespace ui {
2023-05-19 08:16:05 +12:00
template <>
2021-03-07 16:05:23 -06:00
void RecentEntriesTable < APRSRecentEntries >:: draw (
2023-05-19 08:16:05 +12:00
const Entry & entry ,
const Rect & target_rect ,
Painter & painter ,
2025-10-03 19:10:10 +02:00
const Style & style ,
RecentEntriesColumns & columns ) {
2023-05-19 08:16:05 +12:00
Color target_color ;
// auto entry_age = entry.age;
2021-03-07 16:05:23 -06:00
2024-05-27 21:02:52 +02:00
target_color = Theme :: getInstance () -> fg_green -> foreground ;
2023-05-19 08:16:05 +12:00
2023-08-25 20:01:37 -05:00
std :: string entry_string = "" ;
2023-05-19 08:16:05 +12:00
entry_string += entry . source_formatted ;
2025-10-03 19:10:10 +02:00
entry_string . resize ( columns . at ( 0 ). second , ' ' );
entry_string += " " ;
2023-05-19 08:16:05 +12:00
entry_string += ( entry . hits <= 999 ? to_string_dec_uint ( entry . hits , 4 ) : "999+" );
entry_string += " " ;
entry_string += entry . time_string ;
painter . draw_string (
target_rect . location (),
style ,
entry_string );
if ( entry . has_position ) {
painter . draw_bitmap ( target_rect . location () + Point ( 12 * 8 , 0 ), bitmap_target , target_color , style . background );
}
2021-03-07 16:05:23 -06:00
}
void APRSRxView :: focus () {
2023-05-19 08:16:05 +12:00
options_region . focus ();
2021-03-07 16:05:23 -06:00
}
2023-05-19 08:16:05 +12:00
APRSRxView :: APRSRxView ( NavigationView & nav , Rect parent_rect )
2023-06-18 12:11:04 -07:00
: View ( parent_rect ), nav_ { nav } {
2023-05-19 08:16:05 +12:00
baseband :: run_image ( portapack :: spi_flash :: image_tag_aprs_rx );
2022-04-15 08:43:44 -04:00
2023-05-19 08:16:05 +12:00
add_children ({ & rssi ,
& channel ,
& field_rf_amp ,
& field_lna ,
& field_vga ,
2023-06-05 11:09:50 -07:00
& field_volume ,
2023-05-19 08:16:05 +12:00
& options_region ,
& field_frequency ,
& record_view ,
& console });
2022-04-15 08:43:44 -04:00
2023-05-19 08:16:05 +12:00
// DEBUG
record_view . on_error = [ & nav ]( std :: string message ) {
nav . display_modal ( "Error" , message );
};
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
record_view . set_sampling_rate ( 24000 );
options_region . on_change = [ this ]( size_t , int32_t i ) {
2024-11-05 14:56:38 +01:00
field_frequency . on_change = [ this ]( rf :: Frequency f ) { ( void ) f ; };
if ( i == 0 ) { // MAN Manual setting
field_frequency . set_value ( aprs_rx_freq );
} else if ( i == 1 ) { // NA - North America - is also the default
2023-05-19 08:16:05 +12:00
field_frequency . set_value ( 144390000 );
2025-01-26 17:39:38 -03:00
} else if ( i == 2 ) { // NZ
2023-05-19 08:16:05 +12:00
field_frequency . set_value ( 144575000 );
2025-01-26 17:39:38 -03:00
} else if ( i == 3 ) { // JAP
field_frequency . set_value ( 144640000 );
} else if ( i == 4 ) { // PHI
field_frequency . set_value ( 144740000 );
} else if ( i == 5 ) { // EUR
field_frequency . set_value ( 144800000 );
} else if ( i == 6 ) { // THA
field_frequency . set_value ( 144900000 );
} else if ( i == 7 ) { // AUS
field_frequency . set_value ( 145175000 );
} else if ( i == 8 ) { // BR
field_frequency . set_value ( 145570000 );
} else if ( i == 9 ) { // ISS
2024-01-25 21:10:43 +01:00
field_frequency . set_value ( 145825000 );
2023-05-19 08:16:05 +12:00
}
2024-11-05 14:56:38 +01:00
options_region_id = i ;
receiver_model . set_target_frequency ( field_frequency . value ()); // Retune
field_frequency . on_change = [ this ]( rf :: Frequency f ) {
aprs_rx_freq = f ;
options_region . set_selected_index ( 0 , true );
};
2023-05-19 08:16:05 +12:00
};
field_frequency . set_step ( 100 );
2024-11-05 14:56:38 +01:00
field_frequency . on_edit = [ this ]() {
auto freq_view = nav_ . push < FrequencyKeypadView > ( field_frequency . value ());
freq_view -> on_changed = [ this ]( rf :: Frequency f ) {
aprs_rx_freq = f ;
field_frequency . set_value ( f );
};
};
// Note: setting the region is also going to sef field_frequency.on_change
options_region . set_selected_index ( options_region_id , true );
2023-05-19 08:16:05 +12:00
logger = std :: make_unique < APRSLogger > ();
if ( logger )
2024-03-25 02:44:49 -05:00
logger -> append ( logs_dir / u "APRS.TXT" );
2023-05-19 08:16:05 +12:00
baseband :: set_aprs ( 1200 );
audio :: set_rate ( audio :: Rate :: Hz_24000 );
audio :: output :: start ();
receiver_model . enable ();
2021-03-07 16:05:23 -06:00
}
2024-09-04 20:26:31 +02:00
void APRSRxView :: on_freqchg ( int64_t freq ) {
field_frequency . set_value ( freq );
2024-11-05 14:56:38 +01:00
aprs_rx_freq = freq ;
options_region . set_selected_index ( 0 , true );
2024-09-04 20:26:31 +02:00
}
2023-05-19 08:16:05 +12:00
void APRSRxView :: on_packet ( const APRSPacketMessage * message ) {
std :: string str_console = " \x1B " ;
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
aprs :: APRSPacket packet = message -> packet ;
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
std :: string stream_text = packet . get_stream_text ();
str_console += ( char )(( console_color ++ & 3 ) + 9 );
str_console += stream_text ;
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
if ( logger ) {
logger -> log_raw_data ( stream_text );
}
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
// if(reset_console){ //having more than one console causes issues when switching tabs where one is disabled, and the other enabled breaking the scoll setup.
// console.on_hide();
// console.on_show();
// reset_console = false;
// }
console . writeln ( str_console );
2021-03-07 16:05:23 -06:00
}
void APRSRxView :: on_data ( uint32_t value , bool is_data ) {
2023-05-19 08:16:05 +12:00
std :: string str_console = " \x1B " ;
std :: string str_byte = "" ;
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
if ( is_data ) {
// Colorize differently after message splits
str_console += ( char )(( console_color & 3 ) + 9 );
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
if ( value == '\n' ) {
// Message split
console . writeln ( "" );
console_color ++ ;
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
if ( logger ) {
logger -> log_raw_data ( str_log );
str_log = "" ;
}
} else {
if (( value >= 32 ) && ( value < 127 )) {
str_console += ( char ) value ; // Printable
str_byte += ( char ) value ;
} else {
str_console += "[" + to_string_hex ( value , 2 ) + "]" ; // Not printable
str_byte += "[" + to_string_hex ( value , 2 ) + "]" ;
}
console . write ( str_console );
if ( logger ) str_log += str_byte ;
}
} else {
}
2021-03-07 16:05:23 -06:00
}
2023-05-19 08:16:05 +12:00
void APRSRxView :: on_show () {
// some bug where the display scroll area is set to the entire screen when switching from the list tab with details showing back to the stream view.
// reset_console = true;
2021-03-07 16:05:23 -06:00
}
APRSRxView ::~ APRSRxView () {
2023-05-19 08:16:05 +12:00
audio :: output :: stop ();
receiver_model . disable ();
baseband :: shutdown ();
2021-03-07 16:05:23 -06:00
}
void APRSTableView :: on_show_list () {
2023-05-19 08:16:05 +12:00
details_view . hidden ( true );
recent_entries_view . hidden ( false );
send_updates = false ;
focus ();
2021-03-07 16:05:23 -06:00
}
void APRSTableView :: on_show_detail ( const APRSRecentEntry & entry ) {
2023-05-19 08:16:05 +12:00
recent_entries_view . hidden ( true );
details_view . hidden ( false );
details_view . set_entry ( entry );
details_view . update ();
details_view . focus ();
detailed_entry_key = entry . key ();
send_updates = true ;
2021-03-07 16:05:23 -06:00
}
2023-05-19 08:16:05 +12:00
APRSTableView :: APRSTableView ( NavigationView & nav , Rect parent_rec )
: View ( parent_rec ), nav_ { nav } {
add_children ({ & recent_entries_view ,
& details_view });
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
hidden ( true );
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
details_view . hidden ( true );
2021-03-07 16:05:23 -06:00
2025-07-12 13:33:22 +02:00
recent_entries_view . set_parent_rect ({ 0 , 0 , screen_width , screen_height - 40 });
details_view . set_parent_rect ({ 0 , 0 , screen_width , screen_height - 40 });
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
recent_entries_view . on_select = [ this ]( const APRSRecentEntry & entry ) {
this -> on_show_detail ( entry );
};
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
details_view . on_close = [ this ]() {
this -> on_show_list ();
};
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
/* for(size_t i = 0; i <32 ; i++){
std::string id = "test" + i;
auto& entry = ::on_packet(recent, i);
entry.set_source_formatted(id);
}
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
recent_entries_view.set_dirty(); */
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
/*
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
std::string str1 = "test1";
std::string str12 = "test2";
std::string str13 = "test2";
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
auto& entry = ::on_packet(recent, 0x1);
entry.set_source_formatted(str1);
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
auto& entry2 = ::on_packet(recent, 0x2);
entry2.set_source_formatted(str12);
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
auto& entry3 = ::on_packet(recent, 0x2);
entry2.set_source_formatted(str13);
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
recent_entries_view.set_dirty();
*/
2021-03-07 16:05:23 -06:00
}
2023-05-19 08:16:05 +12:00
void APRSTableView :: on_pkt ( const APRSPacketMessage * message ) {
aprs :: APRSPacket packet = message -> packet ;
rtc :: RTC datetime ;
std :: string str_timestamp ;
std :: string source_formatted = packet . get_source_formatted ();
std :: string info_string = packet . get_stream_text ();
2021-03-07 16:05:23 -06:00
2024-01-21 12:47:28 -06:00
rtc_time :: now ( datetime );
2023-05-19 08:16:05 +12:00
auto & entry = :: on_packet ( recent , packet . get_source ());
entry . reset_age ();
entry . inc_hit ();
str_timestamp = to_string_datetime ( datetime , HMS );
entry . set_time_string ( str_timestamp );
entry . set_info_string ( info_string );
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
entry . set_source_formatted ( source_formatted );
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
if ( entry . has_position && ! packet . has_position ()) {
// maintain position info
} else {
entry . set_has_position ( packet . has_position ());
entry . set_pos ( packet . get_position ());
}
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
if ( entry . key () == details_view . entry (). key ()) {
details_view . set_entry ( entry );
details_view . update ();
}
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
recent_entries_view . set_dirty ();
2021-03-07 16:05:23 -06:00
}
2023-05-19 08:16:05 +12:00
void APRSTableView :: on_show () {
on_show_list ();
2021-03-07 16:05:23 -06:00
}
2023-05-19 08:16:05 +12:00
void APRSTableView :: on_hide () {
details_view . hidden ( true );
2021-03-07 16:05:23 -06:00
}
2023-05-19 08:16:05 +12:00
void APRSTableView :: focus () {
recent_entries_view . focus ();
2021-03-07 16:05:23 -06:00
}
2023-05-19 08:16:05 +12:00
APRSTableView ::~ APRSTableView () {
2021-03-07 16:05:23 -06:00
}
void APRSDetailsView :: focus () {
2023-05-19 08:16:05 +12:00
button_done . focus ();
2021-03-07 16:05:23 -06:00
}
2023-05-19 08:16:05 +12:00
void APRSDetailsView :: set_entry ( const APRSRecentEntry & entry ) {
entry_copy = entry ;
2021-03-07 16:05:23 -06:00
}
2023-05-19 08:16:05 +12:00
void APRSDetailsView :: update () {
if ( ! hidden ()) {
// uint32_t age = entry_copy.age;
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
console . clear ( true );
console . write ( entry_copy . info_string );
button_see_map . hidden ( ! entry_copy . has_position );
}
if ( send_updates )
2024-01-05 13:44:30 +01:00
geomap_view -> update_position ( entry_copy . pos . latitude , entry_copy . pos . longitude , 0 , 0 , 0 );
2021-03-07 16:05:23 -06:00
}
APRSDetailsView ::~ APRSDetailsView () {
}
APRSDetailsView :: APRSDetailsView (
2023-05-19 08:16:05 +12:00
NavigationView & nav ) {
add_children ({ & console ,
& button_done ,
& button_see_map });
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
button_done . on_select = [ this , & nav ]( Button & ) {
console . clear ( true );
this -> on_close ();
};
2021-03-07 16:05:23 -06:00
2023-05-19 08:16:05 +12:00
button_see_map . on_select = [ this , & nav ]( Button & ) {
geomap_view = nav . push < GeoMapView > (
entry_copy . source_formatted ,
0 , // entry_copy.pos.altitude,
GeoPos :: alt_unit :: FEET ,
2024-01-05 13:44:30 +01:00
GeoPos :: spd_unit :: HIDDEN ,
2023-05-19 08:16:05 +12:00
entry_copy . pos . latitude ,
entry_copy . pos . longitude ,
0 , /*entry_copy.velo.heading,*/
[ this ]() {
send_updates = false ;
hidden ( false );
update ();
});
send_updates = true ;
hidden ( true );
};
2021-03-07 16:05:23 -06:00
};
2023-05-19 08:16:05 +12:00
APRSRXView :: APRSRXView ( NavigationView & nav )
: nav_ { nav } {
add_children ({ & tab_view ,
& view_stream ,
& view_table });
2021-03-07 16:05:23 -06:00
}
2023-05-19 08:16:05 +12:00
void APRSRXView :: focus () {
tab_view . focus ();
2021-03-07 16:05:23 -06:00
}
APRSRXView ::~ APRSRXView () {
}
} /* namespace ui */