2015-12-02 01:04:04 -05:00
/*
* Copyright ( C ) 2014 Jared Boone , ShareBrained Technology , Inc .
2018-05-21 13:46:48 -04:00
* Copyright ( C ) 2018 Furrtek
2015-12-02 01:04:04 -05:00
*
* 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 .
*/
2015-12-08 18:28:33 -05:00
# include "analog_audio_app.hpp"
2015-12-02 01:04:04 -05:00
2023-06-19 01:48:29 -04:00
# include "audio.hpp"
2016-06-24 14:30:54 -04:00
# include "baseband_api.hpp"
2023-06-19 01:48:29 -04:00
# include "file.hpp"
2015-12-02 01:04:04 -05:00
# include "portapack.hpp"
2016-02-10 18:34:33 -05:00
# include "portapack_persistent_memory.hpp"
2023-06-19 01:48:29 -04:00
# include "string_format.hpp"
2023-07-17 14:43:37 -04:00
# include "ui_freqman.hpp"
2015-12-02 01:04:04 -05:00
# include "utility.hpp"
2024-03-09 17:46:38 -05:00
# include "radio.hpp"
2015-12-02 01:04:04 -05:00
2023-06-19 01:48:29 -04:00
using namespace portapack ;
using namespace tonekey ;
2023-05-10 07:16:23 -04:00
2016-01-31 12:13:44 -05:00
namespace ui {
2016-02-02 16:42:00 -05:00
/* AMOptionsView *********************************************************/
AMOptionsView : : AMOptionsView (
2023-06-19 01:48:29 -04:00
Rect parent_rect ,
const Style * style )
2023-05-18 16:16:05 -04:00
: View { parent_rect } {
set_style ( style ) ;
add_children ( {
& label_config ,
& options_config ,
} ) ;
freqman_set_bandwidth_option ( AM_MODULATION , options_config ) ; // adding the common message from freqman.cpp to the options_config
2023-06-06 12:46:08 -04:00
options_config . set_by_value ( receiver_model . am_configuration ( ) ) ;
options_config . on_change = [ this ] ( size_t , OptionsField : : value_t n ) {
2023-05-18 16:16:05 -04:00
receiver_model . set_am_configuration ( n ) ;
} ;
2016-02-02 16:42:00 -05:00
}
/* NBFMOptionsView *******************************************************/
NBFMOptionsView : : NBFMOptionsView (
2023-06-19 01:48:29 -04:00
Rect parent_rect ,
const Style * style )
2023-05-18 16:16:05 -04:00
: View { parent_rect } {
set_style ( style ) ;
add_children ( { & label_config ,
& options_config ,
& text_squelch ,
& field_squelch } ) ;
freqman_set_bandwidth_option ( NFM_MODULATION , options_config ) ; // adding the common message from freqman.cpp to the options_config
2023-06-06 12:46:08 -04:00
options_config . set_by_value ( receiver_model . nbfm_configuration ( ) ) ;
options_config . on_change = [ this ] ( size_t , OptionsField : : value_t n ) {
2023-05-18 16:16:05 -04:00
receiver_model . set_nbfm_configuration ( n ) ;
} ;
field_squelch . set_value ( receiver_model . squelch_level ( ) ) ;
field_squelch . on_change = [ this ] ( int32_t v ) {
receiver_model . set_squelch_level ( v ) ;
} ;
2016-02-02 16:42:00 -05:00
}
2023-05-07 10:08:45 -04:00
/* WFMOptionsView *******************************************************/
WFMOptionsView : : WFMOptionsView (
2023-06-19 01:48:29 -04:00
Rect parent_rect ,
const Style * style )
2023-05-18 16:16:05 -04:00
: View { parent_rect } {
set_style ( style ) ;
add_children ( {
& label_config ,
& options_config ,
} ) ;
freqman_set_bandwidth_option ( WFM_MODULATION , options_config ) ; // adding the common message from freqman.cpp to the options_config
2023-06-06 12:46:08 -04:00
options_config . set_by_value ( receiver_model . wfm_configuration ( ) ) ;
options_config . on_change = [ this ] ( size_t , OptionsField : : value_t n ) {
2023-05-18 16:16:05 -04:00
receiver_model . set_wfm_configuration ( n ) ;
} ;
2023-05-07 10:08:45 -04:00
}
2020-07-31 06:47:40 -04:00
/* SPECOptionsView *******************************************************/
SPECOptionsView : : SPECOptionsView (
2023-05-18 16:16:05 -04:00
AnalogAudioView * view ,
2023-06-19 01:48:29 -04:00
Rect parent_rect ,
const Style * style )
2023-05-18 16:16:05 -04:00
: View { parent_rect } {
set_style ( style ) ;
2024-03-09 17:46:38 -05:00
add_children ( {
& label_config ,
& options_config ,
& text_speed ,
& field_speed ,
& text_rx_cal ,
2024-03-09 18:36:48 -05:00
& field_rx_iq_phase_cal ,
2024-03-09 17:46:38 -05:00
} ) ;
2023-05-18 16:16:05 -04:00
options_config . set_selected_index ( view - > get_spec_bw_index ( ) ) ;
options_config . on_change = [ this , view ] ( size_t n , OptionsField : : value_t bw ) {
view - > set_spec_bw ( n , bw ) ;
} ;
field_speed . set_value ( view - > get_spec_trigger ( ) ) ;
field_speed . on_change = [ this , view ] ( int32_t v ) {
view - > set_spec_trigger ( v ) ;
} ;
2024-03-09 17:46:38 -05:00
2024-03-09 18:36:48 -05:00
field_rx_iq_phase_cal . set_range ( 0 , hackrf_r9 ? 63 : 31 ) ; // max2839 has 6 bits [0..63], max2837 has 5 bits [0..31]
field_rx_iq_phase_cal . set_value ( view - > get_spec_iq_phase_calibration_value ( ) ) ; // using accessor function of AnalogAudioView to read iq_phase_calibration_value from rx_audio.ini
field_rx_iq_phase_cal . on_change = [ this , view ] ( int32_t v ) {
view - > set_spec_iq_phase_calibration_value ( v ) ; // using accessor function of AnalogAudioView to write inside SPEC submenu, register value to max283x and save it to rx_audio.ini
} ;
2024-03-10 15:50:48 -04:00
view - > set_spec_iq_phase_calibration_value ( view - > get_spec_iq_phase_calibration_value ( ) ) ; // initialize iq_phase_calibration in radio
2020-07-31 06:47:40 -04:00
}
2016-01-31 12:13:44 -05:00
/* AnalogAudioView *******************************************************/
AnalogAudioView : : AnalogAudioView (
2023-05-18 16:16:05 -04:00
NavigationView & nav )
: nav_ ( nav ) {
2023-07-04 19:26:26 -04:00
// A baseband image _must_ be running before add waterfall view.
2023-06-19 01:48:29 -04:00
baseband : : run_image ( portapack : : spi_flash : : image_tag_wideband_spectrum ) ;
2023-05-18 16:16:05 -04:00
add_children ( { & rssi ,
& channel ,
& audio ,
& field_frequency ,
& field_lna ,
& field_vga ,
& options_modulation ,
& field_volume ,
& text_ctcss ,
& record_view ,
& waterfall } ) ;
// Filename Datetime and Frequency
record_view . set_filename_date_frequency ( true ) ;
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 ( ) ;
} ;
2023-06-19 01:48:29 -04:00
auto modulation = receiver_model . modulation ( ) ;
// This app doesn't handle "Capture" mode.
if ( modulation > ReceiverModel : : Mode : : SpectrumAnalysis )
modulation = ReceiverModel : : Mode : : SpectrumAnalysis ;
2023-05-18 16:16:05 -04:00
2023-06-19 01:48:29 -04:00
options_modulation . set_by_value ( toUType ( modulation ) ) ;
2023-05-18 16:16:05 -04:00
options_modulation . on_change = [ this ] ( size_t , OptionsField : : value_t v ) {
this - > on_modulation_changed ( static_cast < ReceiverModel : : Mode > ( v ) ) ;
} ;
options_modulation . on_show_options = [ this ] ( ) {
this - > on_show_options_modulation ( ) ;
} ;
record_view . on_error = [ & nav ] ( std : : string message ) {
nav . display_modal ( " Error " , message ) ;
} ;
waterfall . on_select = [ this ] ( int32_t offset ) {
2023-06-11 14:47:13 -04:00
field_frequency . set_value ( receiver_model . target_frequency ( ) + offset ) ;
2023-05-18 16:16:05 -04:00
} ;
audio : : output : : start ( ) ;
2023-06-19 01:48:29 -04:00
// This call starts the correct baseband image to run
// and sets the radio up as necessary for the given modulation.
on_modulation_changed ( modulation ) ;
2016-01-31 12:13:44 -05:00
}
2023-07-04 19:26:26 -04:00
AnalogAudioView : : AnalogAudioView (
NavigationView & nav ,
ReceiverModel : : settings_t override )
: AnalogAudioView ( nav ) {
2024-03-30 13:57:43 -04:00
// Settings to override when launched from another app (versus from AppSettings .ini file)
2023-07-04 19:26:26 -04:00
// TODO: Which other settings make sense to override?
2024-03-30 13:57:43 -04:00
field_frequency . set_value ( override . frequency_app_override ) ;
2023-07-04 19:26:26 -04:00
on_frequency_step_changed ( override . frequency_step ) ;
options_modulation . set_by_value ( toUType ( override . mode ) ) ;
}
2020-07-31 06:47:40 -04:00
size_t AnalogAudioView : : get_spec_bw_index ( ) {
2023-05-18 16:16:05 -04:00
return spec_bw_index ;
2020-07-31 06:47:40 -04:00
}
void AnalogAudioView : : set_spec_bw ( size_t index , uint32_t bw ) {
2023-05-18 16:16:05 -04:00
spec_bw_index = index ;
spec_bw = bw ;
2020-07-31 06:47:40 -04:00
2023-05-18 16:16:05 -04:00
baseband : : set_spectrum ( bw , spec_trigger ) ;
receiver_model . set_sampling_rate ( bw ) ;
receiver_model . set_baseband_bandwidth ( bw / 2 ) ;
2020-07-31 06:47:40 -04:00
}
2024-03-09 17:46:38 -05:00
uint8_t AnalogAudioView : : get_spec_iq_phase_calibration_value ( ) { // define accessor functions inside AnalogAudioView to read & write real iq_phase_calibration_value
return iq_phase_calibration_value ;
}
void AnalogAudioView : : set_spec_iq_phase_calibration_value ( uint8_t cal_value ) { // define accessor functions
iq_phase_calibration_value = cal_value ;
radio : : set_rx_max283x_iq_phase_calibration ( iq_phase_calibration_value ) ;
}
2020-07-31 06:47:40 -04:00
uint16_t AnalogAudioView : : get_spec_trigger ( ) {
2023-05-18 16:16:05 -04:00
return spec_trigger ;
2020-07-31 06:47:40 -04:00
}
void AnalogAudioView : : set_spec_trigger ( uint16_t trigger ) {
2023-05-18 16:16:05 -04:00
spec_trigger = trigger ;
2020-07-31 06:47:40 -04:00
2023-05-18 16:16:05 -04:00
baseband : : set_spectrum ( spec_bw , spec_trigger ) ;
2020-07-31 06:47:40 -04:00
}
2016-01-31 12:13:44 -05:00
AnalogAudioView : : ~ AnalogAudioView ( ) {
2023-05-18 16:16:05 -04:00
audio : : output : : stop ( ) ;
receiver_model . disable ( ) ;
baseband : : shutdown ( ) ;
2016-01-31 12:13:44 -05:00
}
2023-06-19 01:48:29 -04:00
void AnalogAudioView : : set_parent_rect ( Rect new_parent_rect ) {
2023-05-18 16:16:05 -04:00
View : : set_parent_rect ( new_parent_rect ) ;
2023-06-19 01:48:29 -04:00
ui : : Rect waterfall_rect { 0 , header_height , new_parent_rect . width ( ) , new_parent_rect . height ( ) - header_height } ;
2023-05-18 16:16:05 -04:00
waterfall . set_parent_rect ( waterfall_rect ) ;
2016-01-31 12:13:44 -05:00
}
void AnalogAudioView : : focus ( ) {
2023-05-18 16:16:05 -04:00
field_frequency . focus ( ) ;
2016-01-31 12:13:44 -05:00
}
void AnalogAudioView : : on_baseband_bandwidth_changed ( uint32_t bandwidth_hz ) {
2023-05-18 16:16:05 -04:00
receiver_model . set_baseband_bandwidth ( bandwidth_hz ) ;
2016-01-31 12:13:44 -05:00
}
2023-06-19 01:48:29 -04:00
void AnalogAudioView : : on_modulation_changed ( ReceiverModel : : Mode modulation ) {
// This app doesn't know what to do with "Capture" mode.
if ( modulation > ReceiverModel : : Mode : : SpectrumAnalysis )
modulation = ReceiverModel : : Mode : : SpectrumAnalysis ;
baseband : : spectrum_streaming_stop ( ) ;
2023-05-18 16:16:05 -04:00
update_modulation ( modulation ) ;
on_show_options_modulation ( ) ;
2023-06-19 01:48:29 -04:00
baseband : : spectrum_streaming_start ( ) ;
2016-01-31 12:13:44 -05:00
}
2016-02-02 17:26:00 -05:00
void AnalogAudioView : : remove_options_widget ( ) {
2023-05-18 16:16:05 -04:00
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 ) ;
2016-02-02 17:26:00 -05:00
}
void AnalogAudioView : : set_options_widget ( std : : unique_ptr < Widget > new_widget ) {
2023-05-18 16:16:05 -04:00
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.
2024-05-27 15:02:52 -04:00
options_widget = std : : make_unique < Rectangle > ( options_view_rect , Theme : : getInstance ( ) - > option_active - > background ) ;
2023-05-18 16:16:05 -04:00
}
add_child ( options_widget . get ( ) ) ;
2016-02-02 17:26:00 -05:00
}
void AnalogAudioView : : on_show_options_frequency ( ) {
2024-05-27 15:02:52 -04:00
auto widget = std : : make_unique < FrequencyOptionsView > ( options_view_rect , Theme : : getInstance ( ) - > option_active ) ;
2023-05-18 16:16:05 -04:00
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 ) ) ;
2024-05-27 15:02:52 -04:00
field_frequency . set_style ( Theme : : getInstance ( ) - > option_active ) ;
2016-01-31 12:13:44 -05:00
}
void AnalogAudioView : : on_show_options_rf_gain ( ) {
2024-05-27 15:02:52 -04:00
auto widget = std : : make_unique < RadioGainOptionsView > ( options_view_rect , Theme : : getInstance ( ) - > option_active ) ;
2016-01-31 12:13:44 -05:00
2023-05-18 16:16:05 -04:00
set_options_widget ( std : : move ( widget ) ) ;
2024-05-27 15:02:52 -04:00
field_lna . set_style ( Theme : : getInstance ( ) - > option_active ) ;
2016-01-31 12:13:44 -05:00
}
2016-02-02 16:42:00 -05:00
void AnalogAudioView : : on_show_options_modulation ( ) {
2023-05-18 16:16:05 -04:00
std : : unique_ptr < Widget > widget ;
2023-06-19 01:48:29 -04:00
const auto modulation = receiver_model . modulation ( ) ;
2023-05-18 16:16:05 -04:00
switch ( modulation ) {
case ReceiverModel : : Mode : : AMAudio :
2024-05-27 15:02:52 -04:00
widget = std : : make_unique < AMOptionsView > ( options_view_rect , Theme : : getInstance ( ) - > option_active ) ;
2023-05-18 16:16:05 -04:00
waterfall . show_audio_spectrum_view ( false ) ;
text_ctcss . hidden ( true ) ;
break ;
case ReceiverModel : : Mode : : NarrowbandFMAudio :
2024-05-27 15:02:52 -04:00
widget = std : : make_unique < NBFMOptionsView > ( nbfm_view_rect , Theme : : getInstance ( ) - > option_active ) ;
2023-05-18 16:16:05 -04:00
waterfall . show_audio_spectrum_view ( false ) ;
text_ctcss . hidden ( false ) ;
break ;
case ReceiverModel : : Mode : : WidebandFMAudio :
2024-05-27 15:02:52 -04:00
widget = std : : make_unique < WFMOptionsView > ( options_view_rect , Theme : : getInstance ( ) - > option_active ) ;
2023-05-18 16:16:05 -04:00
waterfall . show_audio_spectrum_view ( true ) ;
text_ctcss . hidden ( true ) ;
break ;
case ReceiverModel : : Mode : : SpectrumAnalysis :
2024-05-27 15:02:52 -04:00
widget = std : : make_unique < SPECOptionsView > ( this , nbfm_view_rect , Theme : : getInstance ( ) - > option_active ) ;
2023-05-18 16:16:05 -04:00
waterfall . show_audio_spectrum_view ( false ) ;
text_ctcss . hidden ( true ) ;
break ;
default :
2023-06-19 01:48:29 -04:00
chDbgPanic ( " Unhandled Mode " ) ;
2023-05-18 16:16:05 -04:00
break ;
}
set_options_widget ( std : : move ( widget ) ) ;
2024-05-27 15:02:52 -04:00
options_modulation . set_style ( Theme : : getInstance ( ) - > option_active ) ;
2016-02-02 16:42:00 -05:00
}
2016-01-31 12:13:44 -05:00
void AnalogAudioView : : on_frequency_step_changed ( rf : : Frequency f ) {
2023-05-18 16:16:05 -04:00
receiver_model . set_frequency_step ( f ) ;
field_frequency . set_step ( f ) ;
2016-01-31 12:13:44 -05:00
}
void AnalogAudioView : : on_reference_ppm_correction_changed ( int32_t v ) {
2023-05-18 16:16:05 -04:00
persistent_memory : : set_correction_ppb ( v * 1000 ) ;
2016-01-31 12:13:44 -05:00
}
2023-06-19 01:48:29 -04:00
void AnalogAudioView : : update_modulation ( ReceiverModel : : Mode modulation ) {
2023-05-18 16:16:05 -04:00
audio : : output : : mute ( ) ;
record_view . stop ( ) ;
baseband : : shutdown ( ) ;
portapack : : spi_flash : : image_tag_t image_tag ;
switch ( modulation ) {
case ReceiverModel : : Mode : : AMAudio :
image_tag = portapack : : spi_flash : : image_tag_am_audio ;
break ;
case ReceiverModel : : Mode : : NarrowbandFMAudio :
image_tag = portapack : : spi_flash : : image_tag_nfm_audio ;
break ;
case ReceiverModel : : Mode : : WidebandFMAudio :
image_tag = portapack : : spi_flash : : image_tag_wfm_audio ;
break ;
case ReceiverModel : : Mode : : SpectrumAnalysis :
image_tag = portapack : : spi_flash : : image_tag_wideband_spectrum ;
break ;
default :
2023-06-19 01:48:29 -04:00
chDbgPanic ( " Unhandled Mode " ) ;
break ;
2023-05-18 16:16:05 -04:00
}
baseband : : run_image ( image_tag ) ;
if ( modulation = = ReceiverModel : : Mode : : SpectrumAnalysis ) {
baseband : : set_spectrum ( spec_bw , spec_trigger ) ;
}
const auto is_wideband_spectrum_mode = ( modulation = = ReceiverModel : : Mode : : SpectrumAnalysis ) ;
receiver_model . set_modulation ( modulation ) ;
receiver_model . set_sampling_rate ( is_wideband_spectrum_mode ? spec_bw : 3072000 ) ;
receiver_model . set_baseband_bandwidth ( is_wideband_spectrum_mode ? spec_bw / 2 : 1750000 ) ;
receiver_model . enable ( ) ;
// TODO: This doesn't belong here! There's a better way.
size_t sampling_rate = 0 ;
switch ( modulation ) {
case ReceiverModel : : Mode : : AMAudio :
sampling_rate = 12000 ;
break ;
case ReceiverModel : : Mode : : NarrowbandFMAudio :
sampling_rate = 24000 ;
break ;
case ReceiverModel : : Mode : : WidebandFMAudio :
sampling_rate = 48000 ;
break ;
default :
break ;
}
record_view . set_sampling_rate ( sampling_rate ) ;
if ( ! is_wideband_spectrum_mode ) {
audio : : output : : unmute ( ) ;
}
2016-04-30 14:25:04 -04:00
}
2023-06-19 01:48:29 -04:00
void AnalogAudioView : : handle_coded_squelch ( uint32_t value ) {
2023-07-02 19:53:51 -04:00
text_ctcss . set ( tone_key_string_by_value ( value , text_ctcss . parent_rect ( ) . width ( ) / 8 ) ) ;
2017-11-28 02:52:04 -05:00
}
2024-09-04 14:26:31 -04:00
void AnalogAudioView : : on_freqchg ( int64_t freq ) {
field_frequency . set_value ( freq ) ;
}
2016-01-31 12:13:44 -05:00
} /* namespace ui */