diff --git a/Makefile b/Makefile index d29e43a..02b87c7 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ ENABLE_NOAA := 0 ENABLE_VOICE := 0 ENABLE_MUTE_RADIO_FOR_VOICE := 0 # Tx on Voice 1.0 kB -ENABLE_VOX := 1 +ENABLE_VOX := 0 ENABLE_VOX_MORE_SENSITIVE := 1 ENABLE_REDUCE_LOW_MID_TX_POWER := 1 # Tx Alarm 600 B @@ -59,7 +59,7 @@ ENABLE_DTMF_CALL_FLASH_LIGHT := 1 ENABLE_FLASH_LIGHT_SOS_TONE := 0 ENABLE_SHOW_CHARGE_LEVEL := 0 ENABLE_REVERSE_BAT_SYMBOL := 1 -ENABLE_FREQ_SEARCH_LNA := 1 +ENABLE_FREQ_SEARCH_LNA := 0 ENABLE_FREQ_SEARCH_TIMEOUT := 0 ENABLE_CODE_SEARCH_TIMEOUT := 0 ENABLE_SCAN_IGNORE_LIST := 1 @@ -82,7 +82,7 @@ ENABLE_TX_AUDIO_BAR := 0 ENABLE_SIDE_BUTT_MENU := 0 # Key Lock 400 B ENABLE_KEYLOCK := 1 -ENABLE_PANADAPTER := 0 +ENABLE_PANADAPTER := 1 #ENABLE_SINGLE_VFO_CHAN := 0 ############################################################# @@ -223,6 +223,9 @@ OBJS += ui/status.o OBJS += ui/ui.o OBJS += version.o OBJS += main.o +ifeq ($(ENABLE_PANADAPTER),1) + OBJS += panadapter.o +endif ifeq ($(OS), Windows_NT) TOP := $(dir $(realpath $(lastword $(MAKEFILE_LIST)))) diff --git a/README.md b/README.md index c304e3c..aadd16b 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ ENABLE_RX_SIGNAL_BAR := 1 enable a menu option for showing an ENABLE_TX_AUDIO_BAR := 1 enable a menu option for showing a TX audio level bar ENABLE_SIDE_BUTT_MENU := 1 enable menu option for configuring the programmable side buttons ENABLE_KEYLOCK := 1 enable keylock menu option + keylock code -#ENABLE_PANADAPTER := 0 not yet implemented - spectrum/pan-adapter +ENABLE_PANADAPTER := 1 TEST ONLY centered on the selected VFO RX frequency, only shows if dual-watch is disabled #ENABLE_SINGLE_VFO_CHAN := 0 not yet implemented - single VFO on display when possible ``` diff --git a/app/app.c b/app/app.c index ecc1dc0..c2facde 100644 --- a/app/app.c +++ b/app/app.c @@ -59,6 +59,9 @@ #include "mdc1200.h" #endif #include "misc.h" +#ifdef ENABLE_PANADAPTER + #include "panadapter.h" +#endif #include "radio.h" #include "settings.h" #if defined(ENABLE_OVERLAY) @@ -71,13 +74,6 @@ #include "ui/status.h" #include "ui/ui.h" -// original QS front end register settings -// 0x03BE 00000 011 101 11 110 -const uint8_t orig_lnas = 3; // 0dB -const uint8_t orig_lna = 5; // -4dB -const uint8_t orig_mixer = 3; // 0dB -const uint8_t orig_pga = 6; // -3dB - static void APP_process_key(const key_code_t Key, const bool key_pressed, const bool key_held); static void APP_update_rssi(const int vfo, const bool force) @@ -88,8 +84,11 @@ static void APP_update_rssi(const int vfo, const bool force) #ifdef ENABLE_AM_FIX // add RF gain adjust compensation - if (g_current_vfo->channel.mod_mode != MOD_MODE_FM && g_eeprom.config.setting.am_fix) - rssi -= rssi_gain_diff[vfo]; + #ifdef ENABLE_PANADAPTER + if (!g_pan_enabled || g_panadapter_vfo_mode > 0) + #endif + if (g_current_vfo->channel.mod_mode != MOD_MODE_FM && g_eeprom.config.setting.am_fix) + rssi -= rssi_gain_diff[vfo]; #endif if (g_current_rssi[vfo] == rssi && !force) @@ -1841,6 +1840,11 @@ void APP_process_power_save(void) { bool power_save = true; + #ifdef ENABLE_PANADAPTER + if (g_eeprom.config.setting.panadapter) + power_save = false; + #endif + if (g_monitor_enabled || #ifdef ENABLE_FMRADIO g_fm_radio_mode || @@ -1861,8 +1865,8 @@ void APP_process_power_save(void) #ifdef ENABLE_NOAA if (IS_NOAA_CHANNEL(g_eeprom.config.setting.indices.vfo[0].screen) || - IS_NOAA_CHANNEL(g_eeprom.config.setting.indices.vfo[1].screen) || - g_noaa_mode) + IS_NOAA_CHANNEL(g_eeprom.config.setting.indices.vfo[1].screen) || + g_noaa_mode) { power_save = false; } @@ -1872,6 +1876,7 @@ void APP_process_power_save(void) { // if (g_current_function == FUNCTION_POWER_SAVE && g_rx_idle_mode) // BK4819_RX_TurnOn(); + if (g_current_function == FUNCTION_POWER_SAVE) FUNCTION_Select(FUNCTION_RECEIVE); // come out of power save mode @@ -2511,6 +2516,11 @@ void APP_time_slice_10ms(void) } #endif + #ifdef ENABLE_PANADAPTER + if (g_eeprom.config.setting.panadapter) + PAN_process_10ms(); + #endif + APP_process_power_save(); APP_process_scan(); diff --git a/app/app.h b/app/app.h index 2a90b06..c4e9fb0 100644 --- a/app/app.h +++ b/app/app.h @@ -23,11 +23,6 @@ #include "frequencies.h" #include "radio.h" -extern const uint8_t orig_lnas; -extern const uint8_t orig_lna; -extern const uint8_t orig_mixer; -extern const uint8_t orig_pga; - void APP_end_tx(void); void APP_stop_scan(void); void APP_channel_next(const bool remember_current, const scan_state_dir_t scan_direction); diff --git a/app/menu.c b/app/menu.c index e386ce8..e1b1d9b 100644 --- a/app/menu.c +++ b/app/menu.c @@ -43,6 +43,9 @@ #include "ui/inputbox.h" #include "ui/menu.h" #include "ui/menu.h" +#ifdef ENABLE_PANADAPTER + #include "panadapter.h" +#endif #include "radio.h" #include "settings.h" #include "ui/ui.h" @@ -229,6 +232,9 @@ int MENU_GetLimits(uint8_t Cursor, int32_t *pMin, int32_t *pMax) #ifdef ENABLE_AM_FIX // case MENU_AM_FIX: #endif + #ifdef ENABLE_PANADAPTER + case MENU_PANADAPTER: + #endif #ifdef ENABLE_TX_AUDIO_BAR case MENU_TX_BAR: #endif @@ -670,6 +676,13 @@ void MENU_AcceptSetting(void) g_flag_reconfigure_vfos = true; break; + #ifdef ENABLE_PANADAPTER + case MENU_PANADAPTER: + g_eeprom.config.setting.panadapter = g_sub_menu_selection; + PAN_enable((g_eeprom.config.setting.panadapter != 0) ? true : false); + break; + #endif + #ifdef ENABLE_TX_AUDIO_BAR case MENU_TX_BAR: g_eeprom.config.setting.mic_bar = g_sub_menu_selection; @@ -1095,11 +1108,11 @@ void MENU_ShowCurrentSetting(void) g_sub_menu_selection = g_eeprom.config.setting.tx_timeout; break; -#ifdef ENABLE_VOICE + #ifdef ENABLE_VOICE case MENU_VOICE: g_sub_menu_selection = g_eeprom.config.setting.voice_prompt; break; -#endif + #endif case MENU_SCAN_CAR_RESUME: g_sub_menu_selection = g_eeprom.config.setting.carrier_search_mode; @@ -1141,17 +1154,23 @@ void MENU_ShowCurrentSetting(void) g_sub_menu_selection = g_eeprom.config.setting.mic_sensitivity; break; -#ifdef ENABLE_TX_AUDIO_BAR + #ifdef ENABLE_PANADAPTER + case MENU_PANADAPTER: + g_sub_menu_selection = g_eeprom.config.setting.panadapter; + break; + #endif + + #ifdef ENABLE_TX_AUDIO_BAR case MENU_TX_BAR: g_sub_menu_selection = g_eeprom.config.setting.mic_bar; break; -#endif + #endif -#ifdef ENABLE_RX_SIGNAL_BAR + #ifdef ENABLE_RX_SIGNAL_BAR case MENU_RX_BAR: g_sub_menu_selection = g_eeprom.config.setting.enable_rssi_bar; break; -#endif + #endif case MENU_COMPAND: g_sub_menu_selection = g_tx_vfo->channel.compand; diff --git a/app/spectrum.c b/app/spectrum.c deleted file mode 100644 index c439c4a..0000000 --- a/app/spectrum.c +++ /dev/null @@ -1,1362 +0,0 @@ -/* Copyright 2023 fagci - * https://github.com/fagci - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "../app/spectrum.h" - -#define F_MIN FrequencyBandTable[0].lower -#define F_MAX FrequencyBandTable[ARRAY_SIZE(FrequencyBandTable) - 1].upper - -const uint16_t RSSI_MAX_VALUE = 65535; - -static uint32_t initialFreq; -static char String[32]; - -bool isInitialized = false; -bool monitorMode = false; -bool redrawStatus = true; -bool redrawScreen = false; -bool newScanStart = true; -bool preventKeypress = true; - -bool isListening = false; -bool isTransmitting = false; - -State currentState = SPECTRUM, previousState = SPECTRUM; - -PeakInfo peak; -ScanInfo scanInfo; -KeyboardState kbd = {KEY_INVALID, KEY_INVALID, 0}; - -const char *bwOptions[] = {" 25k", "12.5k", "6.25k"}; -const char *modulationTypeOptions[] = {" FM", " AM", "USB"}; -const uint8_t modulationTypeTuneSteps[] = {100, 50, 10}; -const uint8_t modTypeReg47Values[] = {1, 7, 5}; - -SpectrumSettings settings = { - .stepsCount = STEPS_64, - .scanStepIndex = S_STEP_25_0kHz, - .frequencyChangeStep = 80000, - .scanDelay = 3200, - .rssiTriggerLevel = 150, - .backlightState = true, - .bw = BK4819_FILTER_BW_WIDE, - .listenBw = BK4819_FILTER_BW_WIDE, - .modulationType = false, -}; - -uint32_t fMeasure = 0; -uint32_t fTx = 0; -uint32_t currentFreq; -uint32_t tempFreq; -uint16_t rssiHistory[128] = {0}; -bool blacklist[128] = {false}; - -static const RegisterSpec afOutRegSpec = {"AF OUT", 0x47, 8, 0xF, 1}; -static const RegisterSpec afDacGainRegSpec = {"AF DAC G", 0x48, 0, 0xF, 1}; -static const RegisterSpec registerSpecs[] = { - {}, - {"LNAs", 0x13, 8, 0b11, 1}, - {"LNA", 0x13, 5, 0b111, 1}, - {"PGA", 0x13, 0, 0b111, 1}, - {"MIX", 0x13, 3, 0b11, 1}, - {"DEV", 0x40, 0, 4095, 1}, - {"CMP", 0x31, 3, 1, 1}, - {"MIC", 0x7D, 0, 0x1F, 1}, -}; - -static uint16_t registersBackup[128]; -static const uint8_t registersToBackup[] = { - 0x13, 0x30, 0x31, 0x37, 0x3D, 0x40, 0x43, 0x47, 0x48, 0x7D, 0x7E, -}; - -static MovingAverage mov = {{128}, {}, 255, 128, 0, 0}; -static const uint8_t MOV_N = ARRAY_SIZE(mov.buf); - -const uint8_t FREQ_INPUT_LENGTH = 10; -uint8_t freqInputIndex = 0; -uint8_t freqInputDotIndex = 0; -key_code_t freqInputArr[10]; -char freqInputString[] = "----------"; // XXXX.XXXXX - -uint8_t menuState = 0; - -uint16_t listenT = 0; - -uint16_t batteryUpdateTimer = 0; -bool isMovingInitialized = false; -uint8_t lastStepsCount = 0; - -uint8_t CountBits(uint16_t n) -{ - uint8_t count = 0; - while (n) - { - count++; - n >>= 1; - } - return count; -} - -static uint16_t GetRegMask(RegisterSpec s) -{ - return (1 << CountBits(s.maxValue)) - 1; -} - -static uint16_t GetRegValue(RegisterSpec s) -{ - return (BK4819_ReadRegister(s.num) >> s.offset) & s.maxValue; -} - -static void SetRegValue(RegisterSpec s, uint16_t v) -{ - uint16_t reg = BK4819_ReadRegister(s.num); - reg &= ~(GetRegMask(s) << s.offset); - BK4819_WriteRegister(s.num, reg | (v << s.offset)); -} - -static void UpdateRegMenuValue(RegisterSpec s, bool add) -{ - uint16_t v = GetRegValue(s); - - if (add && v <= s.maxValue - s.inc) - v += s.inc; - else - if (!add && v >= 0 + s.inc) - v -= s.inc; - - SetRegValue(s, v); - redrawScreen = true; -} - -// Utility functions - -key_code_t GetKey() -{ - key_code_t btn = KEYBOARD_Poll(); - if (btn == KEY_INVALID && !GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT)) - btn = KEY_PTT; - return btn; -} - -void SetState(State state) -{ - previousState = currentState; - currentState = state; - redrawScreen = true; - redrawStatus = true; -} - -// Radio functions - -static void BackupRegisters() -{ - for (int i = 0; i < ARRAY_SIZE(registersToBackup); ++i) - { - uint8_t regNum = registersToBackup[i]; - registersBackup[regNum] = BK4819_ReadRegister(regNum); - } -} - -static void RestoreRegisters() -{ - for (int i = 0; i < ARRAY_SIZE(registersToBackup); ++i) - { - uint8_t regNum = registersToBackup[i]; - BK4819_WriteRegister(regNum, registersBackup[regNum]); - } -} - -static void SetModulation(ModulationType type) -{ - // restore only registers, which we affect here fully - BK4819_WriteRegister(0x37, registersBackup[0x37]); - BK4819_WriteRegister(0x3D, registersBackup[0x3D]); - BK4819_WriteRegister(0x48, registersBackup[0x48]); - - SetRegValue(afOutRegSpec, modTypeReg47Values[type]); - - if (type == MOD_USB) - { - BK4819_WriteRegister(0x37, 0b0001011000001111); - BK4819_WriteRegister(0x3D, 0b0010101101000101); - BK4819_WriteRegister(0x48, 0b0000001110101000); - } - - if (type == MOD_AM) - SetRegValue(afDacGainRegSpec, 0xE); -} - -static void SetF(uint32_t f) { - fMeasure = f; - - BK4819_set_rf_frequency(fMeasure); - BK4819_set_rf_filter_path(fMeasure); - uint16_t reg = BK4819_ReadRegister(BK4819_REG_30); - BK4819_WriteRegister(BK4819_REG_30, 0); - BK4819_WriteRegister(BK4819_REG_30, reg); -} - -static void SetTxF(uint32_t f) { - fTx = f; - BK4819_set_rf_frequency(f); - BK4819_set_rf_filter_path(f); - uint16_t reg = BK4819_ReadRegister(BK4819_REG_30); - BK4819_WriteRegister(BK4819_REG_30, 0); - BK4819_WriteRegister(BK4819_REG_30, reg); -} - -// Spectrum related - -bool IsPeakOverLevel() { return peak.rssi >= settings.rssiTriggerLevel; } - -static void ResetPeak() { - peak.t = 0; - peak.rssi = 0; -} - -bool IsCenterMode() { return settings.scanStepIndex < S_STEP_1_0kHz; } -uint8_t GetStepsCount() { return 128 >> settings.stepsCount; } -uint16_t GetScanStep() { return scanStepValues[settings.scanStepIndex]; } -uint32_t GetBW() { return GetStepsCount() * GetScanStep(); } -uint32_t GetFStart() { - return IsCenterMode() ? currentFreq - (GetBW() >> 1) : currentFreq; -} -uint32_t GetFEnd() { return currentFreq + GetBW(); } - -static void MovingCp(uint16_t *dst, uint16_t *src) { - memcpy(dst, src, GetStepsCount() * sizeof(uint16_t)); -} - -static void ResetMoving() { - for (int i = 0; i < MOV_N; ++i) { - MovingCp(mov.buf[i], rssiHistory); - } -} - -static void MoveHistory() { - const uint8_t XN = GetStepsCount(); - - uint32_t midSum = 0; - - mov.min = RSSI_MAX_VALUE; - mov.max = 0; - - if (lastStepsCount != XN) { - ResetMoving(); - lastStepsCount = XN; - } - for (int i = MOV_N - 1; i > 0; --i) { - MovingCp(mov.buf[i], mov.buf[i - 1]); - } - MovingCp(mov.buf[0], rssiHistory); - - uint8_t skipped = 0; - - for (int x = 0; x < XN; ++x) { - if (blacklist[x]) { - skipped++; - continue; - } - uint32_t sum = 0; - for (int i = 0; i < MOV_N; ++i) { - sum += mov.buf[i][x]; - } - - uint16_t pointV = mov.mean[x] = sum / MOV_N; - - midSum += pointV; - - if (pointV > mov.max) { - mov.max = pointV; - } - - if (pointV < mov.min) { - mov.min = pointV; - } - } - if (skipped == XN) { - return; - } - - mov.mid = midSum / (XN - skipped); -} - -static void TuneToPeak() { - scanInfo.f = peak.f; - scanInfo.rssi = peak.rssi; - scanInfo.i = peak.i; - SetF(scanInfo.f); -} - -uint8_t GetBWRegValueForScan() { - return scanStepBWRegValues[settings.scanStepIndex == S_STEP_100_0kHz ? 11 - : 0]; -} - -uint8_t GetBWRegValueForListen() { - return listenBWRegValues[settings.listenBw]; -} - -static void ResetRSSI() { - uint32_t Reg = BK4819_ReadRegister(BK4819_REG_30); - Reg &= ~1; - BK4819_WriteRegister(BK4819_REG_30, Reg); - Reg |= 1; - BK4819_WriteRegister(BK4819_REG_30, Reg); -} - -uint16_t GetRssi() { - if (currentState == SPECTRUM) { - ResetRSSI(); - SYSTICK_DelayUs(3200); - } - return BK4819_GetRSSI(); -} - -uint32_t GetOffsetedF(uint32_t f) { - switch (gCurrentVfo->FREQUENCY_DEVIATION_SETTING) { - case FREQUENCY_DEVIATION_OFF: - break; - case FREQUENCY_DEVIATION_ADD: - f += gCurrentVfo->FREQUENCY_OF_DEVIATION; - break; - case FREQUENCY_DEVIATION_SUB: - f -= gCurrentVfo->FREQUENCY_OF_DEVIATION; - break; - } - - return Clamp(f, FrequencyBandTable[0].lower, - FrequencyBandTable[ARRAY_SIZE(FrequencyBandTable) - 1].upper); -} - -bool IsTXAllowed() { return g_setting_ALL_TX != 2; } - -static void ToggleAudio(bool on) { - if (on) - GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); - else { - GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER); - } -} - -static void ToggleTX(bool); -static void ToggleRX(bool); - -static void ToggleRX(bool on) { - /* if (isListening == on) { - return; - } */ - isListening = on; - if (on) { - ToggleTX(false); - } - - BK4819_set_GPIO_pin(BK4819_GPIO6_PIN2_GREEN, on); - BK4819_RX_TurnOn(); - - ToggleAudio(on); - BK4819_ToggleAFDAC(on); - BK4819_ToggleAFBit(on); - - if (on) { - listenT = 1000; - BK4819_WriteRegister(0x43, GetBWRegValueForListen()); - } else { - BK4819_WriteRegister(0x43, GetBWRegValueForScan()); - } -} - -uint16_t registersVault[128] = {0}; - -static void RegBackupSet(uint8_t num, uint16_t value) { - registersVault[num] = BK4819_ReadRegister(num); - BK4819_WriteRegister(num, value); -} - -static void RegRestore(uint8_t num) { - BK4819_WriteRegister(num, registersVault[num]); -} - -static void ToggleTX(bool on) { - if (isTransmitting == on) { - return; - } - isTransmitting = on; - if (on) { - ToggleRX(false); - } - - BK4819_set_GPIO_pin(BK4819_GPIO5_PIN1_RED, on); - - if (on) { - ToggleAudio(false); - - SetTxF(GetOffsetedF(fMeasure)); - - RegBackupSet(BK4819_REG_47, 0x6040); - RegBackupSet(BK4819_REG_7E, 0x302E); - RegBackupSet(BK4819_REG_50, 0x3B20); - RegBackupSet(BK4819_REG_37, 0x1D0F); - RegBackupSet(BK4819_REG_52, 0x028F); - RegBackupSet(BK4819_REG_30, 0x0000); - BK4819_WriteRegister(BK4819_REG_30, 0xC1FE); - RegBackupSet(BK4819_REG_51, 0x0000); - - BK4819_SetupPowerAmplifier(gCurrentVfo->TXP_CalculatedSetting, - gCurrentVfo->p_tx->Frequency); - } - else - { - RADIO_tx_eot(); - RADIO_EnableCxCSS(); - - BK4819_SetupPowerAmplifier(0, 0); - - RegRestore(BK4819_REG_51); - BK4819_WriteRegister(BK4819_REG_30, 0); - RegRestore(BK4819_REG_30); - RegRestore(BK4819_REG_52); - RegRestore(BK4819_REG_37); - RegRestore(BK4819_REG_50); - RegRestore(BK4819_REG_7E); - RegRestore(BK4819_REG_47); - - SetF(fMeasure); - } - - BK4819_set_GPIO_pin(BK4819_GPIO0_PIN28_RX_ENABLE, !on); - BK4819_set_GPIO_pin(BK4819_GPIO1_PIN29_PA_ENABLE, on); -} - -// Scan info - -static void ResetScanStats() { - scanInfo.rssi = 0; - scanInfo.rssiMax = 0; - scanInfo.iPeak = 0; - scanInfo.fPeak = 0; -} - -static void InitScan() { - ResetScanStats(); - scanInfo.i = 0; - scanInfo.f = GetFStart(); - - scanInfo.scanStep = GetScanStep(); - scanInfo.measurementsCount = GetStepsCount(); -} - -static void ResetBlacklist() { - for (int i = 0; i < 128; ++i) { - if (blacklist[i]) - blacklist[i] = false; - } -} - -static void RelaunchScan() { - InitScan(); - ResetPeak(); - lastStepsCount = 0; - ToggleRX(false); -#ifdef SPECTRUM_AUTOMATIC_SQUELCH - settings.rssiTriggerLevel = RSSI_MAX_VALUE; -#endif - preventKeypress = true; - scanInfo.rssiMin = RSSI_MAX_VALUE; -} - -static void UpdateScanInfo() { - if (scanInfo.rssi > scanInfo.rssiMax) { - scanInfo.rssiMax = scanInfo.rssi; - scanInfo.fPeak = scanInfo.f; - scanInfo.iPeak = scanInfo.i; - } - - if (scanInfo.rssi < scanInfo.rssiMin) { - scanInfo.rssiMin = scanInfo.rssi; - } -} - -static void AutoTriggerLevel() { - if (settings.rssiTriggerLevel == RSSI_MAX_VALUE) { - settings.rssiTriggerLevel = Clamp(scanInfo.rssiMax + 4, 0, RSSI_MAX_VALUE); - } -} - -static void UpdatePeakInfoForce() { - peak.t = 0; - peak.rssi = scanInfo.rssiMax; - peak.f = scanInfo.fPeak; - peak.i = scanInfo.iPeak; - AutoTriggerLevel(); -} - -static void UpdatePeakInfo() { - if (peak.f == 0 || peak.t >= 1024 || peak.rssi < scanInfo.rssiMax) - UpdatePeakInfoForce(); -} - -static void Measure() { rssiHistory[scanInfo.i] = scanInfo.rssi = GetRssi(); } - -// Update things by keypress - -static void UpdateRssiTriggerLevel(bool inc) { - if (inc) - settings.rssiTriggerLevel += 2; - else - settings.rssiTriggerLevel -= 2; - redrawScreen = true; - SYSTEM_DelayMs(10); -} - -static void ApplyPreset(FreqPreset p) { - currentFreq = p.fStart; - settings.scanStepIndex = p.stepSizeIndex; - settings.listenBw = p.listenBW; - settings.modulationType = p.modulationType; - settings.stepsCount = p.stepsCountIndex; - SetModulation(settings.modulationType); - RelaunchScan(); - ResetBlacklist(); - redrawScreen = true; -} - -static void SelectNearestPreset(bool inc) { - FreqPreset p; - const uint8_t SZ = ARRAY_SIZE(freqPresets); - if (inc) { - for (int i = 0; i < SZ; ++i) { - p = freqPresets[i]; - if (currentFreq < p.fStart) { - ApplyPreset(p); - return; - } - } - } else { - for (int i = SZ - 1; i >= 0; --i) { - p = freqPresets[i]; - if (currentFreq > p.fEnd) { - ApplyPreset(p); - return; - } - } - } - ApplyPreset(p); -} - -static void UpdateScanStep(bool inc) { - if (inc && settings.scanStepIndex < S_STEP_100_0kHz) { - settings.scanStepIndex++; - } else if (!inc && settings.scanStepIndex > 0) { - settings.scanStepIndex--; - } else { - return; - } - settings.frequencyChangeStep = GetBW() >> 1; - RelaunchScan(); - ResetBlacklist(); - redrawScreen = true; -} - -static void UpdateCurrentFreq(bool inc) { - if (inc && currentFreq < F_MAX) { - currentFreq += settings.frequencyChangeStep; - } else if (!inc && currentFreq > F_MIN) { - currentFreq -= settings.frequencyChangeStep; - } else { - return; - } - RelaunchScan(); - ResetBlacklist(); - redrawScreen = true; -} - -static void UpdateCurrentFreqStill(bool inc) { - uint8_t offset = modulationTypeTuneSteps[settings.modulationType]; - uint32_t f = fMeasure; - if (inc && f < F_MAX) { - f += offset; - } else if (!inc && f > F_MIN) { - f -= offset; - } - SetF(f); - redrawScreen = true; -} - -static void UpdateFreqChangeStep(bool inc) { - uint16_t diff = GetScanStep() * 4; - if (inc && settings.frequencyChangeStep < 200000) { - settings.frequencyChangeStep += diff; - } else if (!inc && settings.frequencyChangeStep > 10000) { - settings.frequencyChangeStep -= diff; - } - SYSTEM_DelayMs(100); - redrawScreen = true; -} - -static void ToggleModulation() { - if (settings.modulationType < MOD_USB) { - settings.modulationType++; - } else { - settings.modulationType = MOD_FM; - } - SetModulation(settings.modulationType); - redrawScreen = true; -} - -static void ToggleListeningBW() { - if (settings.listenBw == BK4819_FILTER_BW_NARROWER) { - settings.listenBw = BK4819_FILTER_BW_WIDE; - } else { - settings.listenBw++; - } - redrawScreen = true; -} - -static void ToggleBacklight() { - settings.backlightState = !settings.backlightState; - if (settings.backlightState) { - GPIO_SetBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); - } else { - GPIO_ClearBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT); - } -} - -static void ToggleStepsCount() { - if (settings.stepsCount == STEPS_128) { - settings.stepsCount = STEPS_16; - } else { - settings.stepsCount--; - } - settings.frequencyChangeStep = GetBW() >> 1; - RelaunchScan(); - ResetBlacklist(); - redrawScreen = true; -} - -static void ResetFreqInput() { - tempFreq = 0; - for (int i = 0; i < 10; ++i) { - freqInputString[i] = '-'; - } -} - -static void FreqInput() { - freqInputIndex = 0; - freqInputDotIndex = 0; - ResetFreqInput(); - SetState(FREQ_INPUT); -} - -static void UpdateFreqInput(key_code_t key) { - if (key != KEY_EXIT && freqInputIndex >= 10) { - return; - } - if (key == KEY_STAR) { - if (freqInputIndex == 0 || freqInputDotIndex) { - return; - } - freqInputDotIndex = freqInputIndex; - } - if (key == KEY_EXIT) { - freqInputIndex--; - } else { - freqInputArr[freqInputIndex++] = key; - } - - ResetFreqInput(); - - uint8_t dotIndex = - freqInputDotIndex == 0 ? freqInputIndex : freqInputDotIndex; - - key_code_t digitKey; - for (int i = 0; i < 10; ++i) { - if (i < freqInputIndex) { - digitKey = freqInputArr[i]; - freqInputString[i] = digitKey <= KEY_9 ? '0' + digitKey : '.'; - } else { - freqInputString[i] = '-'; - } - } - - uint32_t base = 100000; // 1MHz in BK units - for (int i = dotIndex - 1; i >= 0; --i) { - tempFreq += freqInputArr[i] * base; - base *= 10; - } - - base = 10000; // 0.1MHz in BK units - if (dotIndex < freqInputIndex) { - for (int i = dotIndex + 1; i < freqInputIndex; ++i) { - tempFreq += freqInputArr[i] * base; - base /= 10; - } - } - redrawScreen = true; -} - -static void Blacklist() { - blacklist[peak.i] = true; - ResetPeak(); - ToggleRX(false); - newScanStart = true; -} - -// Draw things - -static uint8_t Rssi2Y(uint16_t rssi) { - return DrawingEndY - ConvertDomain(rssi, mov.min - 2, - mov.max + 30 + (mov.max - mov.min) / 3, 0, - DrawingEndY); -} - -static void DrawSpectrum() { - for (uint8_t x = 0; x < 128; ++x) { - uint8_t i = x >> settings.stepsCount; - if (blacklist[i]) { - continue; - } - uint16_t rssi = rssiHistory[i]; - DrawHLine(Rssi2Y(rssi), DrawingEndY, x, true); - } -} - -static void UpdateBatteryInfo() { - for (int i = 0; i < 4; i++) { - BOARD_ADC_GetBatteryInfo(&g_battery_voltages[i], &g_usb_current); - } - - uint16_t voltage = Mid(g_battery_voltages, ARRAY_SIZE(g_battery_voltages)); - g_battery_display_level = 0; - - for (int i = ARRAY_SIZE(g_battery_calibration) - 1; i >= 0; --i) { - if (g_battery_calibration[i] < voltage) { - g_battery_display_level = i + 1; - break; - } - } -} - -static void DrawStatus() { - - g_status_line[127] = 0b01111110; - for (int i = 126; i >= 116; i--) { - g_status_line[i] = 0b01000010; - } - uint8_t v = g_battery_display_level; - v <<= 1; - for (int i = 125; i >= 116; i--) { - if (126 - i <= v) { - g_status_line[i + 2] = 0b01111110; - } - } - g_status_line[117] = 0b01111110; - g_status_line[116] = 0b00011000; -} - -static void DrawF(uint32_t f) { - sprintf(String, "%u.%05u", f / 100000, f % 100000); - - if (currentState == STILL && kbd.current == KEY_PTT) { - if (g_battery_display_level == 6) { - sprintf(String, "VOLTAGE HIGH"); - } else if (!IsTXAllowed()) { - sprintf(String, "DISABLED"); - } else { - f = GetOffsetedF(f); - sprintf(String, "TX %u.%05u", f / 100000, f % 100000); - } - } - UI_PrintStringSmall(String, 8, 127, 0); - - sprintf(String, "%s", modulationTypeOptions[settings.modulationType]); - UI_PrintStringSmallest(String, 116, 1, false, true); - sprintf(String, "%s", bwOptions[settings.listenBw]); - UI_PrintStringSmallest(String, 108, 7, false, true); -} - -static void DrawNums() { - if (currentState == SPECTRUM) { - sprintf(String, "%ux", GetStepsCount()); - UI_PrintStringSmallest(String, 0, 1, false, true); - sprintf(String, "%u.%02uk", GetScanStep() / 100, GetScanStep() % 100); - UI_PrintStringSmallest(String, 0, 7, false, true); - } - - if (IsCenterMode()) { - sprintf(String, "%u.%05u \xB1%u.%02uk", currentFreq / 100000, - currentFreq % 100000, settings.frequencyChangeStep / 100, - settings.frequencyChangeStep % 100); - UI_PrintStringSmallest(String, 36, 49, false, true); - } else { - sprintf(String, "%u.%05u", GetFStart() / 100000, GetFStart() % 100000); - UI_PrintStringSmallest(String, 0, 49, false, true); - - sprintf(String, "\xB1%u.%02uk", settings.frequencyChangeStep / 100, - settings.frequencyChangeStep % 100); - UI_PrintStringSmallest(String, 48, 49, false, true); - - sprintf(String, "%u.%05u", GetFEnd() / 100000, GetFEnd() % 100000); - UI_PrintStringSmallest(String, 93, 49, false, true); - } -} - -static void DrawRssiTriggerLevel() { - if (settings.rssiTriggerLevel == RSSI_MAX_VALUE || monitorMode) - return; - uint8_t y = Rssi2Y(settings.rssiTriggerLevel); - for (uint8_t x = 0; x < 128; x += 2) { - PutPixel(x, y, true); - } -} - -static void DrawTicks() { - uint32_t f = GetFStart() % 100000; - uint32_t step = GetScanStep(); - for (uint8_t i = 0; i < 128; i += (1 << settings.stepsCount), f += step) { - uint8_t barValue = 0b00000001; - (f % 10000) < step && (barValue |= 0b00000010); - (f % 50000) < step && (barValue |= 0b00000100); - (f % 100000) < step && (barValue |= 0b00011000); - - g_frame_buffer[5][i] |= barValue; - } - - // center - /* if (IsCenterMode()) { - g_frame_buffer[5][62] = 0x80; - g_frame_buffer[5][63] = 0x80; - g_frame_buffer[5][64] = 0xff; - g_frame_buffer[5][65] = 0x80; - g_frame_buffer[5][66] = 0x80; - } else { - g_frame_buffer[5][0] = 0xff; - g_frame_buffer[5][1] = 0x80; - g_frame_buffer[5][2] = 0x80; - g_frame_buffer[5][3] = 0x80; - g_frame_buffer[5][124] = 0x80; - g_frame_buffer[5][125] = 0x80; - g_frame_buffer[5][126] = 0x80; - g_frame_buffer[5][127] = 0xff; - } */ -} - -static void DrawArrow(uint8_t x) { - for (signed i = -2; i <= 2; ++i) { - signed v = x + i; - uint8_t a = i > 0 ? i : -i; - if (!(v & 128)) { - g_frame_buffer[5][v] |= (0b01111000 << a) & 0b01111000; - } - } -} - -static void DeInitSpectrum() { - SetF(initialFreq); - ToggleRX(false); - RestoreRegisters(); - isInitialized = false; -} - -static void OnKeyDown(uint8_t key) { - switch (key) { - case KEY_3: - SelectNearestPreset(true); - break; - case KEY_9: - SelectNearestPreset(false); - break; - case KEY_1: - UpdateScanStep(true); - break; - case KEY_7: - UpdateScanStep(false); - break; - case KEY_2: - UpdateFreqChangeStep(true); - break; - case KEY_8: - UpdateFreqChangeStep(false); - break; - case KEY_UP: - UpdateCurrentFreq(true); - break; - case KEY_DOWN: - UpdateCurrentFreq(false); - break; - case KEY_SIDE1: - Blacklist(); - break; - case KEY_STAR: - UpdateRssiTriggerLevel(true); - break; - case KEY_F: - UpdateRssiTriggerLevel(false); - break; - case KEY_5: - FreqInput(); - break; - case KEY_0: - ToggleModulation(); - break; - case KEY_6: - ToggleListeningBW(); - break; - case KEY_4: - ToggleStepsCount(); - break; - case KEY_SIDE2: - ToggleBacklight(); - break; - case KEY_PTT: - SetState(STILL); - TuneToPeak(); - settings.rssiTriggerLevel = 120; - break; - case KEY_MENU: - break; - case KEY_EXIT: - if (menuState) { - menuState = 0; - break; - } - DeInitSpectrum(); - break; - default: - break; - } -} - -static void OnKeyDownFreqInput(uint8_t key) { - switch (key) { - case KEY_0: - case KEY_1: - case KEY_2: - case KEY_3: - case KEY_4: - case KEY_5: - case KEY_6: - case KEY_7: - case KEY_8: - case KEY_9: - case KEY_STAR: - UpdateFreqInput(key); - break; - case KEY_EXIT: - if (freqInputIndex == 0) { - SetState(previousState); - break; - } - UpdateFreqInput(key); - break; - case KEY_MENU: - if (tempFreq < F_MIN || tempFreq > F_MAX) { - break; - } - SetState(previousState); - currentFreq = tempFreq; - if (currentState == SPECTRUM) { - ResetBlacklist(); - RelaunchScan(); - } else { - SetF(currentFreq); - } - break; - default: - break; - } -} - -void OnKeyDownStill(key_code_t key) { - switch (key) { - case KEY_3: - break; - case KEY_9: - break; - case KEY_UP: - if (menuState) { - UpdateRegMenuValue(registerSpecs[menuState], true); - break; - } - UpdateCurrentFreqStill(true); - break; - case KEY_DOWN: - if (menuState) { - UpdateRegMenuValue(registerSpecs[menuState], false); - break; - } - UpdateCurrentFreqStill(false); - break; - case KEY_STAR: - UpdateRssiTriggerLevel(true); - break; - case KEY_F: - UpdateRssiTriggerLevel(false); - break; - case KEY_5: - FreqInput(); - break; - case KEY_0: - ToggleModulation(); - break; - case KEY_6: - ToggleListeningBW(); - break; - case KEY_SIDE1: - monitorMode = !monitorMode; - break; - case KEY_SIDE2: - ToggleBacklight(); - break; - case KEY_PTT: - // start transmit - UpdateBatteryInfo(); - if (g_battery_display_level != 6 && IsTXAllowed()) { - ToggleTX(true); - } - redrawScreen = true; - break; - case KEY_MENU: - if (menuState == ARRAY_SIZE(registerSpecs) - 1) { - menuState = 1; - } else { - menuState++; - } - redrawScreen = true; - break; - case KEY_EXIT: - if (menuState) { - menuState = 0; - break; - } - SetState(SPECTRUM); - monitorMode = false; - RelaunchScan(); - break; - default: - break; - } -} - -static void OnKeysReleased() { - if (isTransmitting) { - ToggleTX(false); - } -} - -static void RenderFreqInput() { - UI_PrintString(freqInputString, 2, 127, 0, 8, true); -} - -static void RenderStatus() { - memset(g_status_line, 0, sizeof(g_status_line)); - DrawStatus(); - ST7565_BlitStatusLine(); -} - -static void RenderSpectrum() { - DrawTicks(); - DrawArrow(peak.i << settings.stepsCount); - DrawSpectrum(); - DrawRssiTriggerLevel(); - DrawF(peak.f); - DrawNums(); -} - -static void RenderStill() { - DrawF(fMeasure); - - const uint8_t METER_PAD_LEFT = 3; - - for (int i = 0; i < 121; i++) { - if (i % 10 == 0) { - g_frame_buffer[2][i + METER_PAD_LEFT] = 0b11000000; - } else { - g_frame_buffer[2][i + METER_PAD_LEFT] = 0b01000000; - } - } - - uint8_t x = Rssi2PX(scanInfo.rssi, 0, 121); - for (int i = 0; i < x; ++i) { - if (i % 5 && i / 5 < x / 5) { - g_frame_buffer[2][i + METER_PAD_LEFT] |= 0b00011100; - } - } - - int dbm = Rssi2DBm(scanInfo.rssi); - uint8_t s = DBm2S(dbm); - if (s < 10) { - sprintf(String, "S%u", s); - } else { - sprintf(String, "S9+%u0", s - 9); - } - UI_PrintStringSmallest(String, 4, 10, false, true); - sprintf(String, "%d dBm", dbm); - UI_PrintStringSmallest(String, 32, 10, false, true); - - if (isTransmitting) { - uint8_t afDB = BK4819_ReadRegister(0x6F) & 0b1111111; - uint8_t afPX = ConvertDomain(afDB, 26, 194, 0, 121); - for (int i = 0; i < afPX; ++i) { - g_frame_buffer[3][i + METER_PAD_LEFT] |= 0b00000011; - } - } - - if (!monitorMode) { - uint8_t x = Rssi2PX(settings.rssiTriggerLevel, 0, 121); - g_frame_buffer[2][METER_PAD_LEFT + x - 1] |= 0b01000001; - g_frame_buffer[2][METER_PAD_LEFT + x] = 0b01111111; - g_frame_buffer[2][METER_PAD_LEFT + x + 1] |= 0b01000001; - } - - const uint8_t PAD_LEFT = 4; - const uint8_t CELL_WIDTH = 30; - uint8_t offset = PAD_LEFT; - uint8_t row = 3; - - for (int i = 0, idx = 1; idx <= 7; ++i, ++idx) { - if (idx == 5) { - row += 2; - i = 0; - } - offset = PAD_LEFT + i * CELL_WIDTH; - if (menuState == idx) { - for (int j = 0; j < CELL_WIDTH; ++j) { - g_frame_buffer[row][j + offset] = 0xFF; - g_frame_buffer[row + 1][j + offset] = 0xFF; - } - } - RegisterSpec s = registerSpecs[idx]; - sprintf(String, "%s", s.name); - UI_PrintStringSmallest(String, offset + 2, row * 8 + 2, false, - menuState != idx); - sprintf(String, "%u", GetRegValue(s)); - UI_PrintStringSmallest(String, offset + 2, (row + 1) * 8 + 1, false, - menuState != idx); - } -} - -static void Render() { - memset(g_frame_buffer, 0, sizeof(g_frame_buffer)); - - switch (currentState) { - case SPECTRUM: - RenderSpectrum(); - break; - case FREQ_INPUT: - RenderFreqInput(); - break; - case STILL: - RenderStill(); - break; - } - - ST7565_BlitFullScreen(); -} - -bool HandleUserInput() { - kbd.prev = kbd.current; - kbd.current = GetKey(); - - if (kbd.current == KEY_INVALID) { - kbd.counter = 0; - OnKeysReleased(); - return true; - } - - if (kbd.current == kbd.prev && kbd.counter <= 20) { - kbd.counter++; - SYSTEM_DelayMs(10); - } - - if (kbd.counter == 4 || kbd.counter > 20) { - switch (currentState) { - case SPECTRUM: - OnKeyDown(kbd.current); - break; - case FREQ_INPUT: - OnKeyDownFreqInput(kbd.current); - break; - case STILL: - OnKeyDownStill(kbd.current); - break; - } - } - - return true; -} - -static void Scan() { - if (blacklist[scanInfo.i]) { - return; - } - SetF(scanInfo.f); - Measure(); - UpdateScanInfo(); -} - -static void NextScanStep() { - ++peak.t; - ++scanInfo.i; - scanInfo.f += scanInfo.scanStep; -} - -static void UpdateScan() { - Scan(); - - if (scanInfo.i < scanInfo.measurementsCount) { - NextScanStep(); - return; - } - - MoveHistory(); - - redrawScreen = true; - preventKeypress = false; - - UpdatePeakInfo(); - if (IsPeakOverLevel()) { - ToggleRX(true); - TuneToPeak(); - return; - } - - newScanStart = true; -} - -static void UpdateStill() { - Measure(); - redrawScreen = true; - preventKeypress = false; - - peak.rssi = scanInfo.rssi; - AutoTriggerLevel(); - - ToggleRX(IsPeakOverLevel() || monitorMode); -} - -static void UpdateListening() { - preventKeypress = false; - if (!isListening) { - ToggleRX(true); - } - if (currentState == STILL) { - listenT = 0; - } - if (listenT) { - listenT--; - SYSTEM_DelayMs(1); - return; - } - - if (currentState == SPECTRUM) { - BK4819_WriteRegister(0x43, GetBWRegValueForScan()); - Measure(); - BK4819_WriteRegister(0x43, GetBWRegValueForListen()); - } else { - Measure(); - } - - peak.rssi = scanInfo.rssi; - redrawScreen = true; - - if (IsPeakOverLevel() || monitorMode) { - listenT = 1000; - return; - } - - ToggleRX(false); - newScanStart = true; -} - -static void UpdateTransmitting() {} - -static void Tick() { - if (!preventKeypress) { - HandleUserInput(); - } - if (newScanStart) { - InitScan(); - newScanStart = false; - } - if (isTransmitting) { - UpdateTransmitting(); - } else if (isListening && currentState != FREQ_INPUT) { - UpdateListening(); - } else { - if (currentState == SPECTRUM) { - UpdateScan(); - } else if (currentState == STILL) { - UpdateStill(); - } - } - if (++batteryUpdateTimer > 4096) { - batteryUpdateTimer = 0; - UpdateBatteryInfo(); - redrawStatus = true; - } - if (redrawStatus) { - RenderStatus(); - redrawStatus = false; - } - if (redrawScreen) { - Render(); - redrawScreen = false; - } -} - -static void AutomaticPresetChoose(uint32_t f) { - for (int i = 0; i < ARRAY_SIZE(freqPresets); ++i) { - FreqPreset p = freqPresets[i]; - if (f >= p.fStart && f <= freqPresets[i].fEnd) { - ApplyPreset(p); - } - } -} - -void APP_RunSpectrum() { - BackupRegisters(); - // TX here coz it always? set to active VFO - VFO_Info_t vfo = g_eeprom.vfo_info[g_eeprom.TX_CHANNEL]; - initialFreq = vfo.p_rx->Frequency; - currentFreq = initialFreq; - settings.scanStepIndex = gStepSettingToIndex[vfo.STEP_SETTING]; - settings.listenBw = vfo.CHANNEL_BANDWIDTH == BANDWIDTH_WIDE - ? BANDWIDTH_WIDE - : BANDWIDTH_NARROW; - settings.modulationType = vfo.IsAM ? MOD_AM : MOD_FM; - - AutomaticPresetChoose(currentFreq); - - redrawStatus = true; - redrawScreen = false; // we will wait until scan done - newScanStart = true; - - ToggleRX(true), ToggleRX(false); // hack to prevent noise when squelch off - SetModulation(settings.modulationType); - - RelaunchScan(); - - for (int i = 0; i < 128; ++i) { - rssiHistory[i] = 0; - } - - isInitialized = true; - - while (isInitialized) { - Tick(); - } -} diff --git a/app/spectrum.h b/app/spectrum.h deleted file mode 100644 index 9aeeaf8..0000000 --- a/app/spectrum.h +++ /dev/null @@ -1,203 +0,0 @@ -/* Copyright 2023 fagci - * https://github.com/fagci - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef SPECTRUM_H -#define SPECTRUM_H - -#include -#include -#include - -#include "../bitmaps.h" -#include "../board.h" -#include "../bsp/dp32g030/gpio.h" -#include "../driver/bk4819-regs.h" -#include "../driver/bk4819.h" -#include "../driver/gpio.h" -#include "../driver/keyboard.h" -#include "../driver/st7565.h" -#include "../driver/system.h" -#include "../driver/systick.h" -#include "../external/printf/printf.h" -#include "../font.h" -#include "../frequencies.h" -#include "../helper/battery.h" -#include "../helper/measurements.h" -#include "../misc.h" -#include "../radio.h" -#include "../settings.h" -#include "../ui/helper.h" - -static const uint8_t DrawingEndY = 40; - -static const uint16_t scanStepValues[] = { - 1, 10, 50, 100, 250, 500, 625, 833, 1000, 1250, 2500, 10000, -}; - -static const uint8_t gStepSettingToIndex[] = { - [STEP_2_5kHz] = 4, [STEP_5_0kHz] = 5, [STEP_6_25kHz] = 6, - [STEP_10_0kHz] = 8, [STEP_12_5kHz] = 9, [STEP_25_0kHz] = 10, - [STEP_8_33kHz] = 7, -}; - -static const uint16_t scanStepBWRegValues[12] = { - // RX RXw TX BW - // 0b0 000 000 001 01 1000 - // 1 - 0b0000000001011000, // 6.25 - // 10 - 0b0000000001011000, // 6.25 - // 50 - 0b0000000001011000, // 6.25 - // 100 - 0b0000000001011000, // 6.25 - // 250 - 0b0000000001011000, // 6.25 - // 500 - 0b0010010001011000, // 6.25 - // 625 - 0b0100100001011000, // 6.25 - // 833 - 0b0110110001001000, // 6.25 - // 1000 - 0b0110110001001000, // 6.25 - // 1250 - 0b0111111100001000, // 6.25 - // 2500 - 0b0011011000101000, // 25 - // 10000 - 0b0011011000101000, // 25 -}; - -static const uint16_t listenBWRegValues[] = { - 0b0011011000101000, // 25 - 0b0111111100001000, // 12.5 - 0b0100100001011000, // 6.25 -}; - -typedef enum State { - SPECTRUM, - FREQ_INPUT, - STILL, -} State; - -typedef enum StepsCount { - STEPS_128, - STEPS_64, - STEPS_32, - STEPS_16, -} StepsCount; - -typedef enum ModulationType { - MOD_FM, - MOD_AM, - MOD_USB, -} ModulationType; - -typedef enum ScanStep { - S_STEP_0_01kHz, - S_STEP_0_1kHz, - S_STEP_0_5kHz, - S_STEP_1_0kHz, - - S_STEP_2_5kHz, - S_STEP_5_0kHz, - S_STEP_6_25kHz, - S_STEP_8_33kHz, - S_STEP_10_0kHz, - S_STEP_12_5kHz, - S_STEP_25_0kHz, - S_STEP_100_0kHz, -} ScanStep; - -typedef struct SpectrumSettings { - StepsCount stepsCount; - ScanStep scanStepIndex; - uint32_t frequencyChangeStep; - uint16_t scanDelay; - uint16_t rssiTriggerLevel; - - bool backlightState; - BK4819_FilterBandwidth_t bw; - BK4819_FilterBandwidth_t listenBw; - ModulationType modulationType; -} SpectrumSettings; - -typedef struct KeyboardState { - KEY_Code_t current; - KEY_Code_t prev; - uint8_t counter; -} KeyboardState; - -typedef struct ScanInfo { - uint16_t rssi, rssiMin, rssiMax; - uint8_t i, iPeak; - uint32_t f, fPeak; - uint16_t scanStep; - uint8_t measurementsCount; -} ScanInfo; - -typedef struct RegisterSpec { - const char *name; - uint8_t num; - uint8_t offset; - uint16_t maxValue; - uint16_t inc; -} RegisterSpec; - -typedef struct PeakInfo { - uint16_t t; - uint16_t rssi; - uint8_t i; - uint32_t f; -} PeakInfo; - -typedef struct MovingAverage { - uint16_t mean[128]; - uint16_t buf[4][128]; - uint16_t min, mid, max; - uint16_t t; -} MovingAverage; - -typedef struct FreqPreset { - char name[16]; - uint32_t fStart; - uint32_t fEnd; - StepsCount stepsCountIndex; - uint8_t stepSizeIndex; - ModulationType modulationType; - BK4819_FilterBandwidth_t listenBW; -} FreqPreset; - -static const FreqPreset freqPresets[] = { - {"17m", 1806800, 1831800, STEPS_128, S_STEP_1_0kHz, MOD_USB, BK4819_FILTER_BW_NARROWER}, - {"15m", 2100000, 2145000, STEPS_128, S_STEP_1_0kHz, MOD_USB, BK4819_FILTER_BW_NARROWER}, - {"12m", 2489000, 2514000, STEPS_128, S_STEP_1_0kHz, MOD_USB, BK4819_FILTER_BW_NARROWER}, - {"CB", 2697500, 2785500, STEPS_128, S_STEP_5_0kHz, MOD_FM, BK4819_FILTER_BW_NARROW }, - {"10m", 2800000, 2970000, STEPS_128, S_STEP_1_0kHz, MOD_USB, BK4819_FILTER_BW_NARROWER}, - {"AIR", 11800000, 13500000, STEPS_128, S_STEP_100_0kHz, MOD_AM, BK4819_FILTER_BW_NARROW }, - {"2m", 14400000, 14600000, STEPS_128, S_STEP_25_0kHz, MOD_FM, BK4819_FILTER_BW_NARROW }, - {"JD1", 15175000, 15400000, STEPS_128, S_STEP_25_0kHz, MOD_FM, BK4819_FILTER_BW_NARROW }, - {"JD2", 15500000, 15600000, STEPS_64, S_STEP_25_0kHz, MOD_FM, BK4819_FILTER_BW_NARROW }, - {"LPD", 43307500, 43477500, STEPS_128, S_STEP_25_0kHz, MOD_FM, BK4819_FILTER_BW_WIDE }, - {"PMR", 44600625, 44620000, STEPS_16, S_STEP_12_5kHz, MOD_FM, BK4819_FILTER_BW_NARROW }, - {"FRS/GM 462", 46256250, 46272500, STEPS_16, S_STEP_12_5kHz, MOD_FM, BK4819_FILTER_BW_NARROW }, - {"FRS/GM 467", 46756250, 46771250, STEPS_16, S_STEP_12_5kHz, MOD_FM, BK4819_FILTER_BW_NARROW }, -}; - -void APP_RunSpectrum(void); - -#endif diff --git a/firmware.bin b/firmware.bin index f373e7f..768f240 100644 Binary files a/firmware.bin and b/firmware.bin differ diff --git a/firmware.packed.bin b/firmware.packed.bin index ec6eb02..35af10b 100644 Binary files a/firmware.packed.bin and b/firmware.packed.bin differ diff --git a/freq_ignore.c b/freq_ignore.c index 174da2d..337bea5 100644 --- a/freq_ignore.c +++ b/freq_ignore.c @@ -1,6 +1,4 @@ -#include // abs() - #if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG) #include "driver/uart.h" #endif diff --git a/main.c b/main.c index 05235dc..c7873a5 100644 --- a/main.c +++ b/main.c @@ -44,6 +44,9 @@ #include "mdc1200.h" #endif #include "misc.h" +#ifdef ENABLE_PANADAPTER + #include "panadapter.h" +#endif #include "radio.h" #include "settings.h" #include "ui/helper.h" @@ -240,6 +243,10 @@ void Main(void) AM_fix_init(); #endif + #ifdef ENABLE_PANADAPTER + PAN_enable((g_eeprom.config.setting.panadapter != 0) ? true : false); + #endif + BK4819_set_mic_gain(g_mic_sensitivity_tuning); RADIO_configure_channel(0, VFO_CONFIGURE_RELOAD); diff --git a/misc.c b/misc.c index 672bc3a..e4187cd 100644 --- a/misc.c +++ b/misc.c @@ -231,6 +231,13 @@ int16_t g_current_rssi[2]; uint16_t g_current_glitch[2]; uint16_t g_current_noise[2]; +// original QS front end register settings +// 0x03BE 00000 011 101 11 110 +const uint8_t g_orig_lnas = 3; // 0dB +const uint8_t g_orig_lna = 5; // -4dB +const uint8_t g_orig_mixer = 3; // 0dB +const uint8_t g_orig_pga = 6; // -3dB + // *************************** unsigned int get_RX_VFO(void) diff --git a/misc.h b/misc.h index 1cb4674..ac2def2 100644 --- a/misc.h +++ b/misc.h @@ -321,6 +321,11 @@ extern volatile uint16_t g_boot_tick_10ms; extern uint8_t g_mic_sensitivity_tuning; +extern const uint8_t g_orig_lnas; +extern const uint8_t g_orig_lna; +extern const uint8_t g_orig_mixer; +extern const uint8_t g_orig_pga; + unsigned int get_TX_VFO(void); unsigned int get_RX_VFO(void); void NUMBER_Get(char *pDigits, uint32_t *pInteger); diff --git a/panadapter.c b/panadapter.c new file mode 100644 index 0000000..e45a057 --- /dev/null +++ b/panadapter.c @@ -0,0 +1,138 @@ + +#if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG) + #include "driver/uart.h" +#endif +#include "panadapter.h" +#ifdef ENABLE_FMRADIO + #include "app/fm.h" +#endif +#include "driver/bk4819.h" +#include "functions.h" +#include "misc.h" +#include "radio.h" +#include "settings.h" +#include "ui/main.h" +#include "ui/ui.h" + +bool g_pan_enabled = false; + +// a list of frequencies to ignore/skip when scanning +uint8_t g_panadapter_rssi[PANADAPTER_BINS + 1 + PANADAPTER_BINS]; +int g_panadapter_rssi_index; + +int g_panadapter_vfo_mode; // > 0 if we're currently sampling the VFO + +void PAN_set_freq(void) +{ // set the frequency + + const uint32_t step_size = g_tx_vfo->step_freq; + uint32_t freq = g_tx_vfo->p_rx->frequency; + + if (g_panadapter_vfo_mode <= 0) + { // panadapter mode .. add the bin offset + if (g_panadapter_rssi_index < PANADAPTER_BINS) + freq -= step_size * (PANADAPTER_BINS - g_panadapter_rssi_index); + else + if (g_panadapter_rssi_index > PANADAPTER_BINS) + freq += step_size * (g_panadapter_rssi_index - PANADAPTER_BINS); + } + + BK4819_set_rf_frequency(freq, true); // set the VCO/PLL + //BK4819_set_rf_filter_path(freq); // set the proper LNA/PA filter path + + // default front end gains + if (g_panadapter_vfo_mode <= 0) + BK4819_write_reg(0x13, (g_orig_lnas << 8) | (g_orig_lna << 5) | (g_orig_mixer << 3) | (g_orig_pga << 0)); +} + +void PAN_clear(void) +{ + g_panadapter_rssi_index = 0; + memset(g_panadapter_rssi, 0, sizeof(g_panadapter_rssi)); + + g_panadapter_vfo_mode = 1; + + #if defined(ENABLE_UART) && defined(ENABLE_UART_DEBUG) +// UART_printf("%u\r\n", g_panadapter_rssi_index); + #endif +} + +void PAN_enable(const bool enable) +{ + if (enable && g_eeprom.config.setting.panadapter) + { + if (!g_pan_enabled) + { + PAN_clear(); + g_panadapter_vfo_mode = 0; + PAN_set_freq(); + g_pan_enabled = true; + //g_update_display = true; + UI_DisplayMain_pan(true); + } + } + else + { + if (g_pan_enabled) + { + PAN_clear(); + g_panadapter_vfo_mode = 1; + PAN_set_freq(); + g_pan_enabled = false; + g_update_display = true; + } + } +} + +bool PAN_process_10ms(void) +{ + if (!g_pan_enabled) + return false; + + if (g_current_function == FUNCTION_TRANSMIT || + g_current_function == FUNCTION_POWER_SAVE || + g_current_function == FUNCTION_NEW_RECEIVE || + g_current_function == FUNCTION_RECEIVE) + return false; + + if (g_current_display_screen == DISPLAY_SEARCH || + g_css_scan_mode != CSS_SCAN_MODE_OFF || + g_scan_state_dir != SCAN_STATE_DIR_OFF) + return false; + + #ifdef ENABLE_FMRADIO + if (g_fm_radio_mode) + return false; + #endif + + if (g_squelch_open || g_monitor_enabled) + return false; + + if (g_panadapter_vfo_mode <= 0) + { // save the current RSSI value + const uint16_t rssi = BK4819_GetRSSI(); + g_panadapter_rssi[g_panadapter_rssi_index] = (rssi <= 255) ? rssi : 255; + } + + if (g_panadapter_vfo_mode <= 0) + { + if (++g_panadapter_rssi_index >= (int)ARRAY_SIZE(g_panadapter_rssi)) + g_panadapter_rssi_index = 0; + + // switch back to the VFO frequency once every 16 frequency steps + g_panadapter_vfo_mode = ((g_panadapter_rssi_index & 15u) == 0) ? 1 : 0; + } + else + if (++g_panadapter_vfo_mode >= 8) + { + g_panadapter_vfo_mode = 0; + } + + PAN_set_freq(); + + if (g_panadapter_rssi_index == 0 && g_panadapter_vfo_mode <= 1) + UI_DisplayMain_pan(true); // the last bin value - show the panadapter +// g_update_display = true; + + return (g_panadapter_vfo_mode <= 0) ? true : false; +} diff --git a/panadapter.h b/panadapter.h new file mode 100644 index 0000000..f6abf5f --- /dev/null +++ b/panadapter.h @@ -0,0 +1,37 @@ +/* Copyright 2023 One of Eleven + * https://github.com/DualTachyon + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANADAPTER_H +#define PANADAPTER_H + +#include +#include + +#include "driver/st7565.h" + +// number of bins either side of the VFO RX frequency +#define PANADAPTER_BINS ((LCD_WIDTH / 2) - 1) + +extern bool g_pan_enabled; +extern uint8_t g_panadapter_rssi[PANADAPTER_BINS + 1 + PANADAPTER_BINS]; +extern int g_panadapter_vfo_mode; + +void PAN_clear(void); +void PAN_enable(const bool enable); +bool PAN_process_10ms(void); + +#endif + diff --git a/radio.c b/radio.c index 3d17ac0..b48c017 100644 --- a/radio.c +++ b/radio.c @@ -40,6 +40,9 @@ #include "mdc1200.h" #endif #include "misc.h" +#ifdef ENABLE_PANADAPTER + #include "panadapter.h" +#endif #include "radio.h" #include "settings.h" #include "ui/menu.h" @@ -353,24 +356,49 @@ void RADIO_configure_channel(const unsigned int VFO, const unsigned int configur #ifdef ENABLE_AM_FIX AM_fix_reset(VFO); + if (p_vfo->channel.mod_mode != MOD_MODE_FM && g_eeprom.config.setting.am_fix) { - AM_fix_10ms(VFO); + #ifdef ENABLE_PANADAPTER + if (!g_pan_enabled || g_panadapter_vfo_mode > 0) + { + AM_fix_10ms(VFO); + } + else + { // don't do agc + BK4819_DisableAGC(); + BK4819_write_reg(0x13, (g_orig_lnas << 8) | (g_orig_lna << 5) | (g_orig_mixer << 3) | (g_orig_pga << 0)); + } + #else + AM_fix_10ms(VFO); + #endif } else { // don't do agc in FM mode BK4819_DisableAGC(); - BK4819_write_reg(0x13, (orig_lnas << 8) | (orig_lna << 5) | (orig_mixer << 3) | (orig_pga << 0)); + BK4819_write_reg(0x13, (g_orig_lnas << 8) | (g_orig_lna << 5) | (g_orig_mixer << 3) | (g_orig_pga << 0)); } #else if (p_vfo->mod_mode != MOD_MODE_FM) { - BK4819_EnableAGC(); + #ifdef ENABLE_PANADAPTER + if (!g_pan_enabled || g_panadapter_vfo_mode > 0) + { + BK4819_EnableAGC(); + } + else + { // don't do agc + BK4819_DisableAGC(); + BK4819_write_reg(0x13, (g_orig_lnas << 8) | (g_orig_lna << 5) | (g_orig_mixer << 3) | (g_orig_pga << 0)); + } + #else + BK4819_EnableAGC(); + #endif } else { // don't do agc in FM mode BK4819_DisableAGC(); - BK4819_write_reg(0x13, (orig_lnas << 8) | (orig_lna << 5) | (orig_mixer << 3) | (orig_pga << 0)); + BK4819_write_reg(0x13, (g_orig_lnas << 8) | (g_orig_lna << 5) | (g_orig_mixer << 3) | (g_orig_pga << 0)); } #endif diff --git a/settings.h b/settings.h index 9f9f885..31679a7 100644 --- a/settings.h +++ b/settings.h @@ -362,9 +362,17 @@ typedef struct { uint32_t power_on_password; // #ifdef ENABLE_MDC1200 uint16_t mdc1200_id; // 1of11 - uint8_t unused6[2]; // 0xff's + uint8_t unused6[1]; // 0xff's #else - uint8_t unused6[4]; // 0xff's + uint8_t unused6[3]; // 0xff's + #endif + #ifdef ENABLE_PANADAPTER + struct { + uint8_t panadapter:1; // 1 = enable panadapter + uint8_t unused6a:7; // 0xff + }; + #else + uint8_t unused6a; // 0xff #endif // 0x0EA0 diff --git a/ui/main.c b/ui/main.c index 43cad4b..c55c3c4 100644 --- a/ui/main.c +++ b/ui/main.c @@ -34,6 +34,9 @@ #include "mdc1200.h" #endif #include "misc.h" +#ifdef ENABLE_PANADAPTER + #include "panadapter.h" +#endif #include "radio.h" #include "settings.h" #include "ui/helper.h" @@ -50,6 +53,8 @@ const int rssi_offset_band_123 = -44; const int rssi_offset_band_4567 = -18; +int single_vfo = -1; + center_line_t g_center_line = CENTER_LINE_NONE; // *************************************************************************** @@ -393,6 +398,96 @@ void big_freq(const uint32_t frequency, const unsigned int x, const unsigned int #endif } +#ifdef ENABLE_PANADAPTER + + uint8_t bit_reverse_8(uint8_t n) + { + n = ((n >> 1) & 0x55) | ((n << 1) & 0xAA); + n = ((n >> 2) & 0x33) | ((n << 2) & 0xCC); + n = ((n >> 4) & 0x0F) | ((n << 4) & 0xF0); + return n; + } + + void UI_DisplayMain_pan(const bool now) + { + const unsigned int line = (g_eeprom.config.setting.tx_vfo_num == 0) ? 4 : 0; + uint8_t *base_line = g_frame_buffer[line + 2]; + uint8_t max_rssi = g_panadapter_rssi[0]; + uint8_t min_rssi = g_panadapter_rssi[0]; + uint8_t span_rssi; + unsigned int i; + + if (!g_eeprom.config.setting.panadapter) + return; + if (!g_pan_enabled || single_vfo < 0 || g_current_display_screen != DISPLAY_MAIN) + return; + if (g_squelch_open || g_monitor_enabled) + return; + + for (i = 1; i < ARRAY_SIZE(g_panadapter_rssi); i++) + { + const uint8_t rssi = g_panadapter_rssi[i]; + if (max_rssi < rssi) + max_rssi = rssi; + if (min_rssi > rssi) + min_rssi = rssi; + } + + span_rssi = max_rssi - min_rssi; + if (span_rssi < 20) // minimum vertical range + { + span_rssi = 20; + if (min_rssi > (255 - span_rssi)) + min_rssi = 255 - span_rssi; + max_rssi = min_rssi + span_rssi; + } + + if (now) + { + memset(g_frame_buffer[line + 0], 0, LCD_WIDTH); + memset(g_frame_buffer[line + 1], 0, LCD_WIDTH); + memset(g_frame_buffer[line + 2], 0, LCD_WIDTH); + } + + for (i = 0; i < ARRAY_SIZE(g_panadapter_rssi); i++) + { + uint8_t rssi = g_panadapter_rssi[i]; + uint32_t pixels; + + #if 0 + rssi = (rssi < ((-129 + 160) * 2)) ? 0 : rssi - ((-129 + 160) * 2); // min of -129dBm (S3) + rssi = rssi >> 2; + #else + rssi = ((rssi - min_rssi) * 22) / span_rssi; // 0 ~ 21 + #endif + + rssi += 2; + if (rssi > 21) + rssi = 21; + + pixels = (1u << rssi) - 1; + pixels &= 0xfffffffe; + + base_line[i - (LCD_WIDTH * 2)] = bit_reverse_8(pixels >> 16); + base_line[i - (LCD_WIDTH * 1)] = bit_reverse_8(pixels >> 8); + base_line[i - (LCD_WIDTH * 0)] = bit_reverse_8(pixels >> 0); + } + + // center marker (the VFO frequency) + base_line[(ARRAY_SIZE(g_panadapter_rssi) / 2) - (LCD_WIDTH * 2)] ^= 0xAA; + + // top horizontal line + for (i = 0; i < ARRAY_SIZE(g_panadapter_rssi); i += 2) + base_line[i - (LCD_WIDTH * 2)] |= 1u; + +// sprintf(str, "r %3d g %3u n %3u", rssi, glitch, noise); +// UI_PrintStringSmall(str, 2, 0, line); + + if (now) + ST7565_BlitFullScreen(); + } +#endif + void UI_DisplayMain(void) { #if !defined(ENABLE_BIG_FREQ) && defined(ENABLE_SMALLEST_FONT) @@ -400,7 +495,6 @@ void UI_DisplayMain(void) #endif const unsigned int line0 = 0; // text screen line const unsigned int line1 = 4; - int single_vfo = -1; int main_vfo_num = g_eeprom.config.setting.tx_vfo_num; int current_vfo_num = g_eeprom.config.setting.tx_vfo_num; char str[22]; @@ -408,11 +502,16 @@ void UI_DisplayMain(void) g_center_line = CENTER_LINE_NONE; + single_vfo = -1; + if (g_eeprom.config.setting.dual_watch != DUAL_WATCH_OFF && g_rx_vfo_is_active) current_vfo_num = g_rx_vfo_num; // we're currently monitoring the other VFO -// if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_OFF && g_eeprom.config.setting.cross_vfo == CROSS_BAND_OFF) -// single_vfo = g_eeprom.config.setting.tx_vfo_num; +#ifdef ENABLE_PANADAPTER + if (g_eeprom.config.setting.dual_watch == DUAL_WATCH_OFF && g_eeprom.config.setting.cross_vfo == CROSS_BAND_OFF) + if (!g_squelch_open && !g_monitor_enabled && g_eeprom.config.setting.panadapter) + single_vfo = g_eeprom.config.setting.tx_vfo_num; +#endif // clear the screen memset(g_frame_buffer, 0, sizeof(g_frame_buffer)); @@ -636,6 +735,7 @@ void UI_DisplayMain(void) const unsigned int x = 32; uint32_t frequency = g_vfo_info[vfo_num].p_rx->frequency; + if (g_current_function == FUNCTION_TRANSMIT) { // transmitting current_vfo_num = (g_eeprom.config.setting.cross_vfo == CROSS_BAND_OFF) ? g_rx_vfo_num : g_eeprom.config.setting.tx_vfo_num; @@ -1038,6 +1138,10 @@ void UI_DisplayMain(void) } } + #ifdef ENABLE_PANADAPTER + UI_DisplayMain_pan(false); + #endif + ST7565_BlitFullScreen(); } diff --git a/ui/main.h b/ui/main.h index 875bc79..3d37d69 100644 --- a/ui/main.h +++ b/ui/main.h @@ -35,6 +35,9 @@ extern center_line_t g_center_line; bool UI_DisplayAudioBar(const bool now); #endif void UI_update_rssi(const int rssi, const unsigned int glitch, const unsigned int noise, const unsigned int vfo); +#ifdef ENABLE_PANADAPTER + void UI_DisplayMain_pan(const bool now); +#endif void UI_DisplayMain(void); #endif diff --git a/ui/menu.c b/ui/menu.c index 770f0ae..99c2af1 100644 --- a/ui/menu.c +++ b/ui/menu.c @@ -94,6 +94,9 @@ const t_menu_item g_menu_list[] = {"RP STE", VOICE_ID_INVALID, MENU_RP_STE }, {"MIC GN", VOICE_ID_INVALID, MENU_MIC_GAIN }, {"COMPND", VOICE_ID_INVALID, MENU_COMPAND }, +#ifdef ENABLE_PANADAPTER + {"PANA", VOICE_ID_INVALID, MENU_PANADAPTER }, +#endif #ifdef ENABLE_TX_AUDIO_BAR {"Tx BAR", VOICE_ID_INVALID, MENU_TX_BAR }, #endif @@ -760,6 +763,12 @@ void UI_DisplayMenu(void) break; #endif + #ifdef ENABLE_PANADAPTER + case MENU_PANADAPTER: + strcpy(str, g_sub_menu_off_on[g_sub_menu_selection]); + break; + #endif + #ifdef ENABLE_TX_AUDIO_BAR case MENU_TX_BAR: strcpy(str, g_sub_menu_off_on[g_sub_menu_selection]); diff --git a/ui/menu.h b/ui/menu.h index 962d87c..f318660 100644 --- a/ui/menu.h +++ b/ui/menu.h @@ -84,6 +84,9 @@ enum MENU_RP_STE, MENU_MIC_GAIN, MENU_COMPAND, +#ifdef ENABLE_PANADAPTER + MENU_PANADAPTER, +#endif #ifdef ENABLE_TX_AUDIO_BAR MENU_TX_BAR, #endif