2015-07-08 08:39:24 -07:00
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
2023-07-04 16:26:26 -07:00
* Copyright (C) 2023 Kyle Reed
2015-07-08 08:39:24 -07: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.
*/
#include "receiver_model.hpp"
2016-02-10 20:11:19 -08:00
#include "baseband_api.hpp"
2015-08-04 10:03:18 -07:00
#include "portapack_persistent_memory.hpp"
2016-12-09 18:21:47 +01:00
#include "hackrf_gpio.hpp"
2018-02-22 07:04:19 +00:00
#include "portapack.hpp"
2016-02-05 16:52:34 -08:00
#include "radio.hpp"
2016-02-05 16:25:43 -08:00
#include "audio.hpp"
2016-02-02 13:39:02 -08:00
#include "dsp_fir_taps.hpp"
#include "dsp_iir.hpp"
#include "dsp_iir_config.hpp"
2023-06-04 12:56:46 -07:00
#include "utility.hpp"
2016-02-02 13:39:02 -08:00
2023-06-11 11:47:13 -07:00
using namespace hackrf :: one ;
using namespace portapack ;
2016-02-02 13:39:02 -08:00
namespace {
2025-03-23 23:08:12 +01:00
static constexpr std :: array < baseband :: AMConfig , 12 > am_configs {{
2023-07-04 16:26:26 -07:00
// we config here all the non COMMON parameters to each AM modulation type in RX.
2025-05-05 17:48:01 +02:00
{ taps_6k0_decim_1 , taps_9k0_decim_2 , taps_9k0_dsb_channel , AMConfigureMessage :: Modulation :: DSB , audio_12k_hpf_300hz_config , ( int ) AMConfigureMessage :: Zoom_waterfall :: ZOOM_x_1 }, // AM DSB-C BW 9khz (+-4k5) commercial EU bandwidth .
{ taps_6k0_decim_1 , taps_6k0_decim_2 , taps_6k0_dsb_channel , AMConfigureMessage :: Modulation :: DSB , audio_12k_hpf_300hz_config , ( int ) AMConfigureMessage :: Zoom_waterfall :: ZOOM_x_1 }, // AM DSB-C BW 6khz (+-3k0) narrow AM , ham equipments.
{ taps_6k0_decim_1 , taps_6k0_decim_2 , taps_2k8_usb_channel , AMConfigureMessage :: Modulation :: SSB , audio_12k_hpf_300hz_config , ( int ) AMConfigureMessage :: Zoom_waterfall :: ZOOM_x_1 }, // SSB USB BW 2K8 (+ 2K8) SSB ham equipments.
{ taps_6k0_decim_1 , taps_6k0_decim_2 , taps_2k8_lsb_channel , AMConfigureMessage :: Modulation :: SSB , audio_12k_hpf_300hz_config , ( int ) AMConfigureMessage :: Zoom_waterfall :: ZOOM_x_1 }, // SSB LSB BW 2K8 (- 2K8) SSB ham equipments.
{ taps_6k0_decim_1 , taps_6k0_decim_2 , taps_0k7_usb_channel , AMConfigureMessage :: Modulation :: SSB , audio_12k_hpf_300hz_config , ( int ) AMConfigureMessage :: Zoom_waterfall :: ZOOM_x_1 }, // SSB USB BW 0K7 (+ 0K7) To get audio tone from CW Morse, assuming tx shifted +700hz aprox
{ taps_6k0_decim_1 , taps_6k0_decim_2 , taps_2k6_usb_wefax_channel , AMConfigureMessage :: Modulation :: SSB_FM , apt_audio_12k_lpf_1500hz_config , ( int ) AMConfigureMessage :: Zoom_waterfall :: ZOOM_x_1 }, // SSB USB+FM to demod. Subcarrier FM Audio Tones to get APT Weather Fax.
2025-03-23 23:08:12 +01:00
// below options for Waterfall zoom x 2
2025-05-05 17:48:01 +02:00
{ taps_6k0_narrow_decim_1 , taps_9k0_decim_2 , taps_9k0_dsb_channel , AMConfigureMessage :: Modulation :: DSB , audio_12k_hpf_300hz_config , ( int ) AMConfigureMessage :: Zoom_waterfall :: ZOOM_x_2 }, // AM DSB-C BW 9khz (+-4k5) commercial EU bandwidth .
{ taps_6k0_narrow_decim_1 , taps_6k0_decim_2 , taps_6k0_dsb_channel , AMConfigureMessage :: Modulation :: DSB , audio_12k_hpf_300hz_config , ( int ) AMConfigureMessage :: Zoom_waterfall :: ZOOM_x_2 }, // AM DSB-C BW 6khz (+-3k0) narrow AM , ham equipments.
{ taps_6k0_narrow_decim_1 , taps_6k0_decim_2 , taps_2k8_usb_channel , AMConfigureMessage :: Modulation :: SSB , audio_12k_hpf_300hz_config , ( int ) AMConfigureMessage :: Zoom_waterfall :: ZOOM_x_2 }, // SSB USB BW 2K8 (+ 2K8) SSB ham equipments.
{ taps_6k0_narrow_decim_1 , taps_6k0_decim_2 , taps_2k8_lsb_channel , AMConfigureMessage :: Modulation :: SSB , audio_12k_hpf_300hz_config , ( int ) AMConfigureMessage :: Zoom_waterfall :: ZOOM_x_2 }, // SSB LSB BW 2K8 (- 2K8) SSB ham equipments.
{ taps_6k0_narrow_decim_1 , taps_6k0_decim_2 , taps_0k7_usb_channel , AMConfigureMessage :: Modulation :: SSB , audio_12k_hpf_300hz_config , ( int ) AMConfigureMessage :: Zoom_waterfall :: ZOOM_x_2 }, // SSB USB BW 0K7 (+ 0K7) To get audio tone from CW Morse, assuming tx shifted +700hz aprox
{ taps_6k0_narrow_decim_1 , taps_6k0_decim_2 , taps_2k6_usb_wefax_channel , AMConfigureMessage :: Modulation :: SSB_FM , apt_audio_12k_lpf_1500hz_config , ( int ) AMConfigureMessage :: Zoom_waterfall :: ZOOM_x_2 }, // SSB USB+FM to demod. Subcarrier FM Audio Tones to get APT Weather Fax with waterfall zoom x 2 (we need taps_6k0_narrow_decim_1 to minimize aliasing)
2023-05-19 08:16:05 +12:00
}};
2016-02-02 13:39:02 -08:00
2023-05-19 08:16:05 +12:00
static constexpr std :: array < baseband :: NBFMConfig , 3 > nbfm_configs {{
{ taps_4k25_decim_0 , taps_4k25_decim_1 , taps_4k25_channel , 2500 },
{ taps_11k0_decim_0 , taps_11k0_decim_1 , taps_11k0_channel , 2500 },
{ taps_16k0_decim_0 , taps_16k0_decim_1 , taps_16k0_channel , 5000 },
}};
2016-02-02 13:39:02 -08:00
2023-05-19 08:16:05 +12:00
static constexpr std :: array < baseband :: WFMConfig , 3 > wfm_configs {{
{ taps_200k_wfm_decim_0 , taps_200k_wfm_decim_1 },
{ taps_180k_wfm_decim_0 , taps_180k_wfm_decim_1 },
2025-05-25 20:04:08 +02:00
{ taps_80k_wfm_decim_0 , taps_80k_wfm_decim_1 },
2023-05-19 08:16:05 +12:00
}};
2016-02-02 13:39:02 -08:00
2025-06-02 14:40:13 +02:00
static constexpr std :: array < baseband :: WFMAMConfig , 3 > wfmam_configs {{
{ taps_16k0_decim_0 , taps_80k_wfmam_decim_1 , taps_64_lp_1875_2166 },
{ taps_16k0_decim_0 , taps_38k_wfmam_decim_1 , taps_64_lp_1875_2166 },
{ taps_16k0_decim_0 , taps_38k_wfmam_decim_1 , taps_64_bpf_2k4_bw_2k },
2025-05-05 17:48:01 +02:00
}};
2016-02-02 13:39:02 -08:00
} /* namespace */
2023-06-11 11:47:13 -07:00
rf :: Frequency ReceiverModel :: target_frequency () const {
return persistent_memory :: target_frequency ();
2015-07-08 08:39:24 -07:00
}
2023-06-11 11:47:13 -07:00
void ReceiverModel :: set_target_frequency ( rf :: Frequency f ) {
persistent_memory :: set_target_frequency ( f );
2024-03-30 12:57:43 -05:00
settings_ . frequency_app_override = f ;
2023-05-19 08:16:05 +12:00
update_tuning_frequency ();
2015-07-08 08:39:24 -07:00
}
2023-07-04 16:26:26 -07:00
uint32_t ReceiverModel :: baseband_bandwidth () const {
return settings_ . baseband_bandwidth ;
}
void ReceiverModel :: set_baseband_bandwidth ( uint32_t v ) {
settings_ . baseband_bandwidth = v ;
update_baseband_bandwidth ();
}
uint32_t ReceiverModel :: sampling_rate () const {
return settings_ . sampling_rate ;
}
void ReceiverModel :: set_sampling_rate ( uint32_t v ) {
settings_ . sampling_rate = v ;
update_sampling_rate ();
}
2015-07-08 08:39:24 -07:00
rf :: Frequency ReceiverModel :: frequency_step () const {
2023-07-04 16:26:26 -07:00
return settings_ . frequency_step ;
2015-07-08 08:39:24 -07:00
}
void ReceiverModel :: set_frequency_step ( rf :: Frequency f ) {
2023-07-04 16:26:26 -07:00
settings_ . frequency_step = f ;
}
uint8_t ReceiverModel :: lna () const {
return settings_ . lna_gain_db ;
}
void ReceiverModel :: set_lna ( uint8_t v_db ) {
settings_ . lna_gain_db = v_db ;
update_lna ();
}
uint8_t ReceiverModel :: vga () const {
return settings_ . vga_gain_db ;
}
void ReceiverModel :: set_vga ( uint8_t v_db ) {
settings_ . vga_gain_db = v_db ;
update_vga ();
}
bool ReceiverModel :: rf_amp () const {
return settings_ . rf_amp ;
}
void ReceiverModel :: set_rf_amp ( bool enabled ) {
settings_ . rf_amp = enabled ;
update_rf_amp ();
}
ReceiverModel :: Mode ReceiverModel :: modulation () const {
return settings_ . mode ;
}
void ReceiverModel :: set_modulation ( Mode v ) {
settings_ . mode = v ;
update_modulation ();
}
uint8_t ReceiverModel :: am_configuration () const {
return settings_ . am_config_index ;
}
void ReceiverModel :: set_am_configuration ( uint8_t n ) {
if ( n < am_configs . size ()) {
settings_ . am_config_index = n ;
update_modulation ();
}
}
2025-03-05 04:27:18 +01:00
uint8_t ReceiverModel :: amfm_configuration () const {
return settings_ . amfm_config_index ;
}
void ReceiverModel :: set_amfm_configuration ( uint8_t n ) {
if ( n < am_configs . size ()) {
settings_ . amfm_config_index = n ;
update_modulation ();
}
}
2023-07-04 16:26:26 -07:00
uint8_t ReceiverModel :: nbfm_configuration () const {
return settings_ . nbfm_config_index ;
}
void ReceiverModel :: set_nbfm_configuration ( uint8_t n ) {
if ( n < nbfm_configs . size ()) {
settings_ . nbfm_config_index = n ;
update_modulation ();
}
}
uint8_t ReceiverModel :: wfm_configuration () const {
return settings_ . wfm_config_index ;
}
void ReceiverModel :: set_wfm_configuration ( uint8_t n ) {
if ( n < wfm_configs . size ()) {
settings_ . wfm_config_index = n ;
update_modulation ();
}
}
2025-05-05 17:48:01 +02:00
uint8_t ReceiverModel :: wfmam_configuration () const {
return settings_ . wfmam_config_index ;
}
void ReceiverModel :: set_wfmam_configuration ( uint8_t n ) {
if ( n < wfmam_configs . size ()) {
settings_ . wfmam_config_index = n ;
update_modulation ();
}
}
2023-07-04 16:26:26 -07:00
uint8_t ReceiverModel :: squelch_level () const {
return settings_ . squelch_level ;
}
void ReceiverModel :: set_squelch_level ( uint8_t v ) {
settings_ . squelch_level = v ;
update_modulation ();
2015-07-08 08:39:24 -07:00
}
2018-02-22 07:04:19 +00:00
void ReceiverModel :: set_antenna_bias () {
2023-05-19 08:16:05 +12:00
update_antenna_bias ();
2016-01-22 23:24:20 -08:00
}
2015-07-08 08:39:24 -07:00
volume_t ReceiverModel :: headphone_volume () const {
2023-06-05 11:09:50 -07:00
return persistent_memory :: headphone_volume ();
2015-07-08 08:39:24 -07:00
}
void ReceiverModel :: set_headphone_volume ( volume_t v ) {
2023-06-05 11:09:50 -07:00
persistent_memory :: set_headphone_volume ( v );
2023-05-19 08:16:05 +12:00
update_headphone_volume ();
2015-07-08 08:39:24 -07:00
}
2023-06-11 11:47:13 -07:00
uint8_t ReceiverModel :: normalized_headphone_volume () const {
auto db = ( headphone_volume () - audio :: headphone :: volume_range (). max ). decibel ();
return clip < uint8_t > ( db + 99 , 0 , 99 );
2023-06-04 12:56:46 -07:00
}
2023-06-11 11:47:13 -07:00
void ReceiverModel :: set_normalized_headphone_volume ( uint8_t v ) {
2023-06-05 11:09:50 -07:00
// TODO: Linear map instead to ensure 0 is minimal value or fix volume_range_t::normalize.
2023-06-11 11:47:13 -07:00
v = clip < uint8_t > ( v , 0 , 99 );
2023-06-04 12:56:46 -07:00
auto new_volume = volume_t :: decibel ( v - 99 ) + audio :: headphone :: volume_range (). max ;
set_headphone_volume ( new_volume );
}
2015-07-08 08:39:24 -07:00
void ReceiverModel :: enable () {
2023-05-19 08:16:05 +12:00
enabled_ = true ;
radio :: set_direction ( rf :: Direction :: Receive );
update_tuning_frequency ();
update_antenna_bias ();
update_rf_amp ();
update_lna ();
update_vga ();
update_baseband_bandwidth ();
update_sampling_rate ();
update_modulation ();
2021-05-22 20:57:48 +01:00
2023-06-05 11:09:50 -07:00
// TODO: maybe not the perfect place for this, but it's reasonable.
2023-05-19 08:16:05 +12:00
update_headphone_volume ();
led_rx . on ();
2015-07-08 08:39:24 -07:00
}
2016-01-10 11:10:13 -08:00
void ReceiverModel :: disable () {
2023-05-19 08:16:05 +12:00
enabled_ = false ;
2016-01-23 11:31:50 -08:00
2023-05-19 08:16:05 +12:00
// TODO: Responsibility for enabling/disabling the radio is muddy.
// Some happens in ReceiverModel, some inside radio namespace.
radio :: disable ();
led_rx . off ();
2015-07-08 08:39:24 -07:00
}
2023-07-04 16:26:26 -07:00
void ReceiverModel :: initialize () {
settings_ = settings_t {};
}
void ReceiverModel :: set_configuration_without_update (
Mode new_mode ,
rf :: Frequency new_frequency_step ,
size_t new_am_config_index ,
size_t new_nbfm_config_index ,
size_t new_wfm_config_index ,
2025-05-24 18:18:24 +02:00
size_t new_wfmam_config_index ,
2023-07-04 16:26:26 -07:00
uint8_t new_squelch_level ) {
settings_ . mode = new_mode ;
settings_ . frequency_step = new_frequency_step ;
settings_ . am_config_index = new_am_config_index ;
settings_ . nbfm_config_index = new_nbfm_config_index ;
settings_ . wfm_config_index = new_wfm_config_index ;
2025-05-24 18:18:24 +02:00
settings_ . wfmam_config_index = new_wfmam_config_index ;
2023-07-04 16:26:26 -07:00
settings_ . squelch_level = new_squelch_level ;
}
void ReceiverModel :: configure_from_app_settings (
const app_settings :: AppSettings & settings ) {
settings_ . baseband_bandwidth = settings . baseband_bandwidth ;
settings_ . sampling_rate = settings . sampling_rate ;
settings_ . lna_gain_db = settings . lna ;
settings_ . vga_gain_db = settings . vga ;
settings_ . rf_amp = settings . rx_amp ;
settings_ . squelch_level = settings . squelch ;
}
2015-07-08 08:39:24 -07:00
int32_t ReceiverModel :: tuning_offset () {
2023-05-19 08:16:05 +12:00
if (( modulation () == Mode :: SpectrumAnalysis )) {
return 0 ;
} else {
return - ( sampling_rate () / 4 );
}
2015-07-08 08:39:24 -07:00
}
void ReceiverModel :: update_tuning_frequency () {
2023-06-11 11:47:13 -07:00
// TODO: use positive offset if freq < offset.
2025-03-19 00:31:40 +01:00
radio :: set_tuning_frequency ( target_frequency () + hidden_offset + tuning_offset ());
2015-07-08 08:39:24 -07:00
}
2025-03-19 23:03:04 +01:00
void ReceiverModel :: set_hidden_offset ( rf :: Frequency offset ) {
hidden_offset = offset ;
update_tuning_frequency ();
}
2015-07-08 08:39:24 -07:00
void ReceiverModel :: update_baseband_bandwidth () {
2023-10-08 17:50:31 +02:00
radio :: set_baseband_filter_bandwidth_rx ( baseband_bandwidth ());
2023-06-11 11:47:13 -07:00
}
2016-07-27 14:41:36 -07:00
void ReceiverModel :: update_sampling_rate () {
2023-05-19 08:16:05 +12:00
// TODO: Move more low-level radio control stuff to M4. It'll enable tighter
// synchronization for things like wideband (sweeping) spectrum analysis, and
// protocols that need quick RX/TX turn-around.
2016-01-10 11:01:54 -08:00
2023-05-19 08:16:05 +12:00
// Disabling baseband while changing sampling rates seems like a good idea...
radio :: set_baseband_rate ( sampling_rate ());
update_tuning_frequency ();
2015-07-08 08:39:24 -07:00
}
2023-07-04 16:26:26 -07:00
void ReceiverModel :: update_lna () {
radio :: set_lna_gain ( lna ());
}
void ReceiverModel :: update_vga () {
radio :: set_vga_gain ( vga ());
}
void ReceiverModel :: update_rf_amp () {
radio :: set_rf_amp ( rf_amp ());
2015-07-08 08:39:24 -07:00
}
2016-02-02 13:39:02 -08:00
2016-07-27 14:39:49 -07:00
void ReceiverModel :: update_modulation () {
2023-05-19 08:16:05 +12:00
switch ( modulation ()) {
default :
case Mode :: AMAudio :
update_am_configuration ();
break ;
2016-02-02 13:39:02 -08:00
2025-03-05 04:27:18 +01:00
case Mode :: AMAudioFMApt : // Wefax , first step , USB demodulation from the AMAudio group, index 2 (USB+3K), TODO +FM subcarrier demod ?
update_amfm_configuration ();
break ;
2023-05-19 08:16:05 +12:00
case Mode :: NarrowbandFMAudio :
update_nbfm_configuration ();
break ;
2016-02-02 13:39:02 -08:00
2023-05-19 08:16:05 +12:00
case Mode :: WidebandFMAudio :
update_wfm_configuration ();
break ;
2016-02-02 13:39:02 -08:00
2025-05-05 17:48:01 +02:00
case Mode :: WFMAudioAMApt :
update_wfmam_configuration ();
break ;
2023-05-19 08:16:05 +12:00
case Mode :: SpectrumAnalysis :
case Mode :: Capture :
break ;
}
2016-02-02 13:39:02 -08:00
}
void ReceiverModel :: update_am_configuration () {
2023-07-04 16:26:26 -07:00
am_configs [ am_configuration ()]. apply ();
2016-02-02 15:19:43 -08:00
}
2025-03-05 04:27:18 +01:00
void ReceiverModel :: update_amfm_configuration () {
am_configs [ amfm_configuration ()]. apply (); // update with different index for Wefax.
}
2016-02-02 13:39:02 -08:00
void ReceiverModel :: update_nbfm_configuration () {
2023-07-04 16:26:26 -07:00
nbfm_configs [ nbfm_configuration ()]. apply ( squelch_level ());
2016-02-02 15:19:43 -08:00
}
2016-02-02 13:39:02 -08:00
void ReceiverModel :: update_wfm_configuration () {
2023-07-04 16:26:26 -07:00
wfm_configs [ wfm_configuration ()]. apply ();
}
2025-05-05 17:48:01 +02:00
void ReceiverModel :: update_wfmam_configuration () {
wfmam_configs [ wfmam_configuration ()]. apply (); // update with different index for Wefax.
}
2023-07-04 16:26:26 -07:00
void ReceiverModel :: update_antenna_bias () {
if ( enabled_ )
radio :: set_antenna_bias ( portapack :: get_antenna_bias ());
}
void ReceiverModel :: update_headphone_volume () {
audio :: headphone :: set_volume ( headphone_volume ());
2016-02-02 13:39:02 -08:00
}