Add a one-tap audio mute on the VFO and Channel screens: - SK2 short-tap toggles mute via the built-in audioAmpMute()/audioAmpIsMuted(). The tap is only consumed by the existing "set TX TG to incoming" behaviour while a DMR QSO is on screen, so mute works everywhere else. - Header: a bold "M" is shown just left of the battery while muted (the firmware's built-in strike-through over the mode text also still shows). - LED: flashes red for 0.5s every 2s while muted (main loop), overriding the normal RX/TX LED. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
3913 lines
135 KiB
C
3913 lines
135 KiB
C
/*
|
|
* Copyright (C) 2019-2025 Roger Clark, VK3KYY / G4KYF
|
|
* Daniel Caujolle-Bert, F1RMB
|
|
*
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* 4. Use of this source code or binary releases for commercial purposes is strictly forbidden. This includes, without limitation,
|
|
* incorporation in a commercial product or incorporation into a product or project which allows commercial use.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
#include "user_interface/uiGlobals.h"
|
|
#include "hardware/HR-C6000.h"
|
|
#if defined(PLATFORM_MD9600) || defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
#include "hardware/radioHardwareInterface.h"
|
|
#endif
|
|
#include "functions/trx.h"
|
|
#include "functions/rxPowerSaving.h"
|
|
#include "user_interface/menuSystem.h"
|
|
#include "user_interface/uiUtilities.h"
|
|
#include "user_interface/uiLocalisation.h"
|
|
#include "utils.h"
|
|
|
|
typedef enum
|
|
{
|
|
VFO_SELECTED_FREQUENCY_INPUT_RX,
|
|
VFO_SELECTED_FREQUENCY_INPUT_TX
|
|
} vfoSelectedFrequencyInput_t;
|
|
|
|
typedef enum
|
|
{
|
|
VFO_SCREEN_OPERATION_NORMAL,
|
|
VFO_SCREEN_OPERATION_SCAN,
|
|
VFO_SCREEN_OPERATION_DUAL_SCAN,
|
|
VFO_SCREEN_OPERATION_SWEEP
|
|
} vfoScreenOperationMode_t;
|
|
|
|
typedef enum
|
|
{
|
|
SWEEP_SETTING_STEP = 0,
|
|
SWEEP_SETTING_RSSI,
|
|
SWEEP_SETTING_GAIN
|
|
} sweepSetting_t;
|
|
|
|
// internal prototypes
|
|
static void handleEvent(uiEvent_t *ev);
|
|
static void handleQuickMenuEvent(uiEvent_t *ev);
|
|
static void updateQuickMenuScreen(bool isFirstRun);
|
|
static void updateFrequency(int frequency, bool announceImmediately);
|
|
static void stepFrequency(int increment);
|
|
static void toneScan(void);
|
|
static void scanning(void);
|
|
static void scanInit(void);
|
|
static void sweepScanInit(void);
|
|
static void sweepScanStep(void);
|
|
static void updateTrxID(void );
|
|
static void setCurrentFreqToScanLimits(void);
|
|
static void handleUpKey(uiEvent_t *ev);
|
|
static void handleDownKey(uiEvent_t *ev);
|
|
static void vfoSweepUpdateSamples(int offset, bool forceRedraw, int bandwidthRescale);
|
|
static void setSweepIncDecSetting(sweepSetting_t type, bool increment);
|
|
static void vfoSweepDrawSample(int offset);
|
|
static void clearNuisance(void);
|
|
|
|
static vfoSelectedFrequencyInput_t selectedFreq = VFO_SELECTED_FREQUENCY_INPUT_RX;
|
|
|
|
static const int SCAN_TONE_INTERVAL = 200;//time between each tone for lowest tone. (higher tones take less time.)
|
|
static uint8_t scanToneIndex = 0;
|
|
static CodeplugCSSTypes_t toneScanType = CSS_TYPE_CTCSS;
|
|
static CodeplugCSSTypes_t toneScanCSS = CSS_TYPE_NONE; // Here, CSS_NONE means *ALL* CSS types
|
|
static uint16_t prevCSSTone = (CODEPLUG_CSS_TONE_NONE - 1);
|
|
|
|
static vfoScreenOperationMode_t screenOperationMode[2] = { VFO_SCREEN_OPERATION_NORMAL, VFO_SCREEN_OPERATION_NORMAL };// For VFO A and B
|
|
|
|
static menuStatus_t menuVFOExitStatus = MENU_STATUS_SUCCESS;
|
|
static menuStatus_t menuQuickVFOExitStatus = MENU_STATUS_SUCCESS;
|
|
|
|
static bool quickmenuNewChannelHandled = false; // Quickmenu new channel confirmation window
|
|
|
|
static const int VFO_SWEEP_STEP_TIME = 25;// 25ms
|
|
|
|
#if defined(PLATFORM_RD5R)
|
|
#define VFO_SWEEP_GRAPH_START_Y 8
|
|
#define VFO_SWEEP_GRAPH_HEIGHT_Y 30
|
|
#elif defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
#define VFO_SWEEP_GRAPH_START_Y 10
|
|
#define VFO_SWEEP_GRAPH_HEIGHT_Y 78
|
|
#else
|
|
#define VFO_SWEEP_GRAPH_START_Y 10
|
|
#define VFO_SWEEP_GRAPH_HEIGHT_Y 38
|
|
#endif
|
|
|
|
|
|
static uint8_t vfoSweepSamples[VFO_SWEEP_NUM_SAMPLES];
|
|
static uint8_t vfoSweepRssiNoiseFloor = VFO_SWEEP_RSSI_NOISE_FLOOR_DEFAULT;
|
|
static uint8_t vfoSweepGain = VFO_SWEEP_GAIN_DEFAULT;
|
|
static bool vfoSweepSavedBandwidth;
|
|
const int VFO_SWEEP_SCAN_FREQ_STEP_TABLE[7] = {125,250,500,1000,2500,5000,10000};
|
|
static uint8_t previousVFONumber = 0xFF; // Keep track of the currently loaded channel data
|
|
|
|
|
|
// Public interface
|
|
menuStatus_t uiVFOMode(uiEvent_t *ev, bool isFirstRun)
|
|
{
|
|
static uint32_t m = 0, curm = 0;
|
|
|
|
if (isFirstRun)
|
|
{
|
|
#if ! defined(PLATFORM_GD77S)
|
|
// We're coming back from the lock screen (hence no channel init is needed, at all).
|
|
bool isLockMenu = (menuSystemGetPreviouslyPushedMenuNumber(true) == UI_LOCK_SCREEN);
|
|
if (isLockMenu || lockscreenIsRearming)
|
|
{
|
|
if (isLockMenu)
|
|
{
|
|
menuSystemGetPreviouslyPushedMenuNumber(false); // Clear the previous lock screen trace
|
|
}
|
|
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiVFOModeUpdateScreen(0);
|
|
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SWEEP)
|
|
{
|
|
for(int i = 0; i < VFO_SWEEP_NUM_SAMPLES; i++)
|
|
{
|
|
vfoSweepDrawSample(i);
|
|
}
|
|
|
|
displayDrawFastVLine((uiDataGlobal.Scan.sweepSampleIndex) % VFO_SWEEP_NUM_SAMPLES, VFO_SWEEP_GRAPH_START_Y, VFO_SWEEP_GRAPH_HEIGHT_Y, true);// draw solid line in the next location
|
|
displayDrawFastVLine((uiDataGlobal.Scan.sweepSampleIndex + uiDataGlobal.Scan.sweepSampleIndexIncrement) % VFO_SWEEP_NUM_SAMPLES, VFO_SWEEP_GRAPH_START_Y, VFO_SWEEP_GRAPH_HEIGHT_Y, true);// draw solid line in the next location
|
|
|
|
displayRenderRows(1, ((8 + VFO_SWEEP_GRAPH_HEIGHT_Y) / 8) + 1);
|
|
}
|
|
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
uiDataGlobal.FreqEnter.index = 0;
|
|
|
|
uiDataGlobal.isDisplayingQSOData = false;
|
|
uiDataGlobal.reverseRepeaterVFO = false;
|
|
settingsSet(nonVolatileSettings.initialMenuNumber, (uint8_t) UI_VFO_MODE);
|
|
uiDataGlobal.displayQSOStatePrev = QSO_DISPLAY_IDLE;
|
|
currentChannelData = &settingsVFOChannel[nonVolatileSettings.currentVFONumber];
|
|
currentChannelData->libreDMR_Power = 0x00;// Force channel to the Master power
|
|
|
|
uiDataGlobal.currentSelectedChannelNumber = CH_DETAILS_VFO_CHANNEL;// This is not a regular channel. Its the special VFO channel!
|
|
uiDataGlobal.displayChannelSettings = false;
|
|
uiDataGlobal.talkaround = false;
|
|
|
|
#if defined(PLATFORM_MD9600)
|
|
// This could happen if a MK22 codeplug containing 220MHz channel(s) has been flashed.
|
|
if ((trxGetBandFromFrequency(currentChannelData->rxFreq) == FREQUENCY_OUT_OF_BAND) || (trxGetBandFromFrequency(currentChannelData->txFreq) == FREQUENCY_OUT_OF_BAND))
|
|
{
|
|
currentChannelData->rxFreq = OUT_OF_BAND_FALLBACK_FREQUENCY;
|
|
currentChannelData->txFreq = OUT_OF_BAND_FALLBACK_FREQUENCY;
|
|
}
|
|
#endif
|
|
|
|
radioSetTRxDevice(RADIO_DEVICE_PRIMARY);
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
|
|
#if defined(PLATFORM_MD2017)
|
|
if (true)
|
|
{
|
|
radioSetTRxDevice(RADIO_DEVICE_SECONDARY);
|
|
CodeplugChannel_t *secondaryChannel = &settingsVFOChannel[1 - nonVolatileSettings.currentVFONumber];
|
|
radioSetFrequency(secondaryChannel->rxFreq, false);
|
|
trxSetModeAndBandwidth(secondaryChannel->chMode, (codeplugChannelGetFlag(secondaryChannel, CHANNEL_FLAG_BW_25K) != 0));
|
|
radioSetTRxDevice(RADIO_DEVICE_PRIMARY);
|
|
}
|
|
#endif
|
|
|
|
//Need to load the Rx group if specified even if TG is currently overridden as we may need it later when the left or right button is pressed
|
|
if (currentChannelData->rxGroupList != 0)
|
|
{
|
|
if (currentChannelData->rxGroupList != lastLoadedRxGroup)
|
|
{
|
|
if (codeplugRxGroupGetDataForIndex(currentChannelData->rxGroupList, ¤tRxGroupData))
|
|
{
|
|
lastLoadedRxGroup = currentChannelData->rxGroupList;
|
|
}
|
|
else
|
|
{
|
|
lastLoadedRxGroup = -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memset(¤tRxGroupData, 0xFF, sizeof(CodeplugRxGroup_t));// If the VFO doesnt have an Rx Group ( TG List) the global var needs to be cleared, otherwise it contains the data from the previous screen e.g. Channel screen
|
|
lastLoadedRxGroup = -1;
|
|
}
|
|
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
|
|
lastHeardClearLastID();
|
|
|
|
int nextMenu = menuSystemGetPreviouslyPushedMenuNumber(false); // used to determine if this screen has just been loaded after Tx ended (in loadChannelData()))
|
|
|
|
uiVFOModeLoadChannelData((((nextMenu == UI_TX_SCREEN) || (nextMenu == UI_LOCK_SCREEN) || (nextMenu == UI_PRIVATE_CALL)) ? false : true));
|
|
|
|
if ((uiDataGlobal.displayQSOState == QSO_DISPLAY_CALLER_DATA) && (trxGetMode() == RADIO_MODE_ANALOG))
|
|
{
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
}
|
|
|
|
freqEnterReset();
|
|
uiVFOModeUpdateScreen(0);
|
|
settingsSetVFODirty();
|
|
|
|
if ((uiDataGlobal.VoicePrompts.inhibitInitial == false) &&
|
|
((uiDataGlobal.Scan.active == false) ||
|
|
(uiDataGlobal.Scan.active && ((uiDataGlobal.Scan.state == SCAN_STATE_SHORT_PAUSED) || (uiDataGlobal.Scan.state == SCAN_STATE_PAUSED)))))
|
|
{
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_AND_CONTACT_OR_VFO_FREQ_AND_MODE,
|
|
((nextMenu == UI_TX_SCREEN) || (nextMenu == UI_PRIVATE_CALL)) ? PROMPT_THRESHOLD_NEVER_PLAY_IMMEDIATELY : PROMPT_THRESHOLD_2);
|
|
}
|
|
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SCAN)
|
|
{
|
|
// Refresh on every step if scan boundaries is equal to one frequency step.
|
|
uiDataGlobal.Scan.refreshOnEveryStep = ((nonVolatileSettings.vfoScanHigh[nonVolatileSettings.currentVFONumber] - nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber]) <= VFO_FREQ_STEP_TABLE[(currentChannelData->VFOflag5 >> 4)]);
|
|
}
|
|
|
|
// Need to do this last, as other things in the screen init, need to know whether the main screen has just changed
|
|
if (uiDataGlobal.VoicePrompts.inhibitInitial)
|
|
{
|
|
uiDataGlobal.VoicePrompts.inhibitInitial = false;
|
|
}
|
|
|
|
menuVFOExitStatus = MENU_STATUS_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
menuVFOExitStatus = MENU_STATUS_SUCCESS;
|
|
|
|
#if ! defined(PLATFORM_GD77S)
|
|
// Don't go any further while APRS is beaconing
|
|
if (aprsBeaconingForcedManualBeaconingTriggered() && aprsBeaconingIsTransmitting())
|
|
{
|
|
return menuVFOExitStatus;
|
|
}
|
|
#endif
|
|
|
|
if (ev->events == NO_EVENT)
|
|
{
|
|
bool updateLHDisplay = ((nonVolatileSettings.lastTalkerOnScreenTimer > 0U) &&
|
|
(uiDataGlobal.lastHeardCount > 0) &&
|
|
(uiDataGlobal.Scan.active == false) &&
|
|
ticksTimerIsEnabled(&uiDataGlobal.DMRLastTalkerOnScreen.timer) &&
|
|
ticksTimerHasExpired(&uiDataGlobal.DMRLastTalkerOnScreen.timer) &&
|
|
((audioAmpGetStatus() & AUDIO_AMP_CHANNEL_RF) == 0));
|
|
|
|
// We are entering digits, so update the screen as we have a cursor to blink
|
|
if ((uiDataGlobal.FreqEnter.index > 0) && ((ev->time - curm) > 300))
|
|
{
|
|
curm = ev->time;
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN; // Redraw will happen just below
|
|
}
|
|
|
|
|
|
// is there an incoming DMR signal
|
|
if ((uiDataGlobal.displayQSOState != QSO_DISPLAY_IDLE) || updateLHDisplay)
|
|
{
|
|
if (updateLHDisplay)
|
|
{
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
}
|
|
|
|
uiVFOModeUpdateScreen(0);
|
|
|
|
// Force full redraw, due to Notification hidding while sweep scanning
|
|
if (uiVFOModeSweepScanning(true))
|
|
{
|
|
vfoSweepUpdateSamples(0, true, 0);
|
|
displayRender();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((ev->time - m) > RSSI_UPDATE_COUNTER_RELOAD)
|
|
{
|
|
if (rxPowerSavingIsRxOn())
|
|
{
|
|
bool doRendering = true;
|
|
|
|
if (uiDataGlobal.Scan.active && (screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP) && (uiDataGlobal.Scan.state == SCAN_STATE_PAUSED))
|
|
{
|
|
#if defined(PLATFORM_RD5R)
|
|
displayClearRows(0, 1, false);
|
|
#else
|
|
displayClearRows(0, 2, false);
|
|
#endif
|
|
uiUtilityRenderHeader(false, false, ((ev->buttons & BUTTON_SK1) == BUTTON_SK1));
|
|
}
|
|
else
|
|
{
|
|
if (uiVFOModeDualWatchIsScanning())
|
|
{
|
|
// Header needs to be updated, if Dual Watch is scanning
|
|
uiUtilityRedrawHeaderOnly(true, false, ((ev->buttons & BUTTON_SK1) == BUTTON_SK1));
|
|
doRendering = false;
|
|
}
|
|
else if (uiVFOModeSweepScanning(true) == false)
|
|
{
|
|
uiUtilityDrawRSSIBarGraph();
|
|
}
|
|
}
|
|
|
|
// Only render the second row which contains the bar graph, if we're not scanning,
|
|
// as there is no need to redraw the rest of the screen
|
|
if (doRendering)
|
|
{
|
|
if (uiNotificationIsVisible())
|
|
{
|
|
displayRender();
|
|
}
|
|
else
|
|
{
|
|
displayRenderRows(((uiDataGlobal.Scan.active && (uiDataGlobal.Scan.state == SCAN_STATE_PAUSED)) ? 0 : 1), 2);
|
|
}
|
|
}
|
|
}
|
|
|
|
m = ev->time;
|
|
}
|
|
|
|
}
|
|
|
|
if (uiDataGlobal.Scan.toneActive)
|
|
{
|
|
toneScan();
|
|
}
|
|
|
|
#if ! (defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017))
|
|
if (uiDataGlobal.Scan.active)
|
|
{
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP)
|
|
{
|
|
scanning();
|
|
}
|
|
else
|
|
{
|
|
sweepScanStep();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (ev->hasEvent)
|
|
{
|
|
// Scanning barrier
|
|
if (uiDataGlobal.Scan.toneActive)
|
|
{
|
|
// Left key (alone) reverse tone scan direction
|
|
if ((ev->events & KEY_EVENT) && (BUTTONCHECK_DOWN(ev, BUTTON_SK2) == 0))
|
|
{
|
|
if (
|
|
#if defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380)
|
|
(ev->keys.key == KEY_FRONT_DOWN)
|
|
#else
|
|
(ev->keys.key == KEY_LEFT)
|
|
#endif
|
|
)
|
|
{
|
|
uiDataGlobal.Scan.direction *= -1;
|
|
keyboardReset();
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
#if defined(PLATFORM_RD5R) // virtual ORANGE button will be implemented later, this CPP will be removed then.
|
|
if ((ev->keys.key != 0) && (ev->keys.event & KEY_MOD_UP))
|
|
#else
|
|
// PTT key is already handled in main().
|
|
if (((ev->events & BUTTON_EVENT) && BUTTONCHECK_SHORTUP(ev, BUTTON_ORANGE)) ||
|
|
((ev->keys.key != 0) && (ev->keys.event & KEY_MOD_UP)))
|
|
#endif
|
|
{
|
|
uiVFOModeStopScanning();
|
|
}
|
|
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
|
|
handleEvent(ev);
|
|
}
|
|
}
|
|
|
|
#if defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
if (uiDataGlobal.Scan.active)
|
|
{
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP)
|
|
{
|
|
scanning();
|
|
}
|
|
else
|
|
{
|
|
sweepScanStep();
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
return menuVFOExitStatus;
|
|
}
|
|
|
|
void uiVFOModeUpdateScreen(int txTimeSecs)
|
|
{
|
|
static bool blink = false;
|
|
static uint32_t blinkTime = 0;
|
|
char buffer[SCREEN_LINE_BUFFER_SIZE];
|
|
bool isTransmitting = isTransmittingIgnoringAPRSBeaconing();
|
|
|
|
// We don't want QSO info to be displayed while in Sweep scan, or screen redrawing while Sweep is paused
|
|
if (uiVFOModeSweepScanning(true) && ((uiDataGlobal.displayQSOState >= QSO_DISPLAY_CALLER_DATA) || (uiDataGlobal.Scan.state == SCAN_STATE_PAUSED)))
|
|
{
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_IDLE;
|
|
return;
|
|
}
|
|
|
|
// Only render the header, then wait for the next run
|
|
// Otherwise the screen could remain blank if TG and PC are == 0
|
|
// since uiDataGlobal.displayQSOState won't be set to QSO_DISPLAY_IDLE
|
|
if ((trxGetMode() == RADIO_MODE_DIGITAL) && (HRC6000GetReceivedTgOrPcId() == 0) &&
|
|
((uiDataGlobal.displayQSOState == QSO_DISPLAY_CALLER_DATA) || (uiDataGlobal.displayQSOState == QSO_DISPLAY_CALLER_DATA_UPDATE)))
|
|
{
|
|
uiUtilityRedrawHeaderOnly(uiVFOModeDualWatchIsScanning(), uiVFOModeSweepScanning(true), false);
|
|
return;
|
|
}
|
|
|
|
// We're currently displaying details or entering scan freq limits, and it shouldn't be overridden by QSO data
|
|
if ((uiDataGlobal.displayChannelSettings ||
|
|
((screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SCAN) && (uiDataGlobal.FreqEnter.index != 0)))
|
|
&& ((uiDataGlobal.displayQSOState == QSO_DISPLAY_CALLER_DATA) || (uiDataGlobal.displayQSOState == QSO_DISPLAY_CALLER_DATA_UPDATE)))
|
|
{
|
|
// We will not restore the previous QSO Data as a new caller just arose.
|
|
uiDataGlobal.displayQSOStatePrev = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
}
|
|
|
|
displayClearBuf();
|
|
uiUtilityRenderHeader(uiVFOModeDualWatchIsScanning(), uiVFOModeSweepScanning(true),
|
|
#if defined(PLATFORM_MD9600) || defined(CPU_MK22FN512VLL12)
|
|
false
|
|
#else
|
|
((buttonsRead() & BUTTON_SK1) == BUTTON_SK1)
|
|
#endif
|
|
);
|
|
|
|
switch(uiDataGlobal.displayQSOState)
|
|
{
|
|
case QSO_DISPLAY_DEFAULT_SCREEN:
|
|
lastHeardClearLastID();
|
|
if ((uiDataGlobal.Scan.active &&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_DUAL_SCAN) && (uiDataGlobal.Scan.state == SCAN_STATE_SCANNING)))
|
|
{
|
|
uiUtilityDisplayFrequency(DISPLAY_Y_POS_RX_FREQ, false, false, settingsVFOChannel[CHANNEL_VFO_A].rxFreq, true, true, 1);
|
|
uiUtilityDisplayFrequency(DISPLAY_Y_POS_TX_FREQ, false, false, settingsVFOChannel[CHANNEL_VFO_B].rxFreq, true, true, 2);
|
|
}
|
|
else
|
|
{
|
|
uiDataGlobal.displayQSOStatePrev = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiDataGlobal.isDisplayingQSOData = false;
|
|
uiDataGlobal.receivedPcId = 0x00;
|
|
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP)
|
|
{
|
|
if (uiDataGlobal.displayChannelSettings)
|
|
{
|
|
uint32_t PCorTG = ((nonVolatileSettings.overrideTG != 0) ? nonVolatileSettings.overrideTG : codeplugContactGetPackedId(¤tContactData));
|
|
|
|
snprintf(buffer, SCREEN_LINE_BUFFER_SIZE, "%s %u",
|
|
(((PCorTG >> 24) == PC_CALL_FLAG) ? currentLanguage->pc : currentLanguage->tg),
|
|
(PCorTG & 0xFFFFFF));
|
|
}
|
|
else
|
|
{
|
|
if ((trxTransmissionEnabled == false) && (aprsBeaconingIsTransmitting() == false) &&
|
|
(uiDataGlobal.Scan.active == false) &&
|
|
((nonVolatileSettings.lastTalkerOnScreenTimer > 0U) &&
|
|
ticksTimerHasExpired(&uiDataGlobal.DMRLastTalkerOnScreen.timer))
|
|
)
|
|
{
|
|
ticksTimerStart(&uiDataGlobal.DMRLastTalkerOnScreen.timer, (nonVolatileSettings.lastTalkerOnScreenTimer * 1000U));
|
|
uiDataGlobal.DMRLastTalkerOnScreen.visible = (((audioAmpGetStatus() & AUDIO_AMP_CHANNEL_RF) || (uiDataGlobal.lastHeardCount < 1)) ? false : !uiDataGlobal.DMRLastTalkerOnScreen.visible);
|
|
|
|
if (uiDataGlobal.DMRLastTalkerOnScreen.visible)
|
|
{
|
|
if (uiDataGlobal.lastHeardCount > 0)
|
|
{
|
|
bool displayTA = false;
|
|
LinkItem_t *item = LinkHead;
|
|
|
|
switch (nonVolatileSettings.contactDisplayPriority)
|
|
{
|
|
case CONTACT_DISPLAY_PRIO_CC_DB_TA:
|
|
case CONTACT_DISPLAY_PRIO_DB_CC_TA:
|
|
// No contact found in codeplug and DMRIDs, use TA as fallback, if any.
|
|
if ((strncmp(item->contact, "ID:", 3) == 0) && (item->talkerAlias[0] != 0x00))
|
|
{
|
|
displayTA = true;
|
|
}
|
|
break;
|
|
|
|
case CONTACT_DISPLAY_PRIO_TA_CC_DB:
|
|
case CONTACT_DISPLAY_PRIO_TA_DB_CC:
|
|
if (item->talkerAlias[0] != 0x00)
|
|
{
|
|
displayTA = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (displayTA)
|
|
{
|
|
getCallsignOnly(buffer, item->talkerAlias);
|
|
}
|
|
else
|
|
{
|
|
getCallsignOnly(buffer, item->contact);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto displayContactName;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
goto displayContactName;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
displayContactName:
|
|
|
|
if (nonVolatileSettings.overrideTG != 0)
|
|
{
|
|
uiUtilityBuildTgOrPCDisplayName(buffer, SCREEN_LINE_BUFFER_SIZE);
|
|
uiUtilityDisplayInformation(NULL, DISPLAY_INFO_CONTACT_OVERRIDE_FRAME, (isTransmitting ? DISPLAY_Y_POS_CONTACT_TX_FRAME : -1));
|
|
}
|
|
else
|
|
{
|
|
codeplugUtilConvertBufToString(currentContactData.name, buffer, 16);
|
|
}
|
|
}
|
|
}
|
|
|
|
uiUtilityDisplayInformation(buffer, DISPLAY_INFO_CONTACT, (isTransmitting ? DISPLAY_Y_POS_CONTACT_TX : -1));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ticksTimerIsEnabled(&uiDataGlobal.DMRLastTalkerOnScreen.timer))
|
|
{
|
|
ticksTimerReset(&uiDataGlobal.DMRLastTalkerOnScreen.timer);
|
|
uiDataGlobal.DMRLastTalkerOnScreen.visible = false;
|
|
}
|
|
|
|
// Display some channel settings
|
|
if (uiDataGlobal.displayChannelSettings && (screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP))
|
|
{
|
|
uiUtilityDisplayInformation(NULL, DISPLAY_INFO_CHANNEL_DETAILS, -1);
|
|
}
|
|
|
|
if(uiDataGlobal.Scan.toneActive)
|
|
{
|
|
if (toneScanType == CSS_TYPE_CTCSS)
|
|
{
|
|
snprintf(buffer, SCREEN_LINE_BUFFER_SIZE, "CTCSS %3d.%dHz", currentChannelData->rxTone / 10, currentChannelData->rxTone % 10);
|
|
}
|
|
else if (toneScanType & CSS_TYPE_DCS)
|
|
{
|
|
dcsPrintf(buffer, SCREEN_LINE_BUFFER_SIZE, "DCS ", currentChannelData->rxTone);
|
|
}
|
|
else
|
|
{
|
|
snprintf(buffer, SCREEN_LINE_BUFFER_SIZE, "%s", "TONE ERROR");
|
|
}
|
|
|
|
uiUtilityDisplayInformation(buffer, DISPLAY_INFO_CONTACT, -1);
|
|
}
|
|
|
|
}
|
|
|
|
if (uiDataGlobal.FreqEnter.index == 0)
|
|
{
|
|
if (!isTransmitting)
|
|
{
|
|
uiUtilityDisplayFrequency(((screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SWEEP) ? DISPLAY_Y_POS_TX_FREQ : DISPLAY_Y_POS_RX_FREQ),
|
|
false, (selectedFreq == VFO_SELECTED_FREQUENCY_INPUT_RX),
|
|
(uiDataGlobal.reverseRepeaterVFO ? currentChannelData->txFreq : currentChannelData->rxFreq), true,
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SCAN), 0);
|
|
}
|
|
else
|
|
{
|
|
snprintf(buffer, SCREEN_LINE_BUFFER_SIZE, " %d ", txTimeSecs);
|
|
uiUtilityDisplayInformation(buffer, DISPLAY_INFO_TX_TIMER, -1);
|
|
}
|
|
|
|
if (((screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_NORMAL) ||
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_DUAL_SCAN) ||
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SWEEP)) || trxTransmissionEnabled)
|
|
{
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP)
|
|
{
|
|
uiUtilityDisplayFrequency(DISPLAY_Y_POS_TX_FREQ, true, (selectedFreq == VFO_SELECTED_FREQUENCY_INPUT_TX || trxTransmissionEnabled),
|
|
(uiDataGlobal.reverseRepeaterVFO ? currentChannelData->rxFreq : currentChannelData->txFreq), true, false, 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Low/High scanning freqs
|
|
snprintf(buffer, SCREEN_LINE_BUFFER_SIZE, "%u.%03u", nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber] / 100000, (nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber] - (nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber] / 100000) * 100000)/100);
|
|
|
|
displayPrintAt(2, DISPLAY_Y_POS_TX_FREQ, buffer, FONT_SIZE_3);
|
|
|
|
snprintf(buffer, SCREEN_LINE_BUFFER_SIZE, "%u.%03u", nonVolatileSettings.vfoScanHigh[nonVolatileSettings.currentVFONumber] / 100000, (nonVolatileSettings.vfoScanHigh[nonVolatileSettings.currentVFONumber] - (nonVolatileSettings.vfoScanHigh[nonVolatileSettings.currentVFONumber] / 100000) * 100000)/100);
|
|
|
|
displayPrintAt(DISPLAY_SIZE_X - ((7 * 8) + 2), DISPLAY_Y_POS_TX_FREQ, buffer, FONT_SIZE_3);
|
|
// Scanning direction arrow
|
|
static const int16_t scanDirArrow[2][6] = {
|
|
{ // Down
|
|
59 + DISPLAY_H_OFFSET, (DISPLAY_Y_POS_TX_FREQ + (FONT_SIZE_3_HEIGHT / 2) - 1),
|
|
67 + DISPLAY_H_OFFSET, (DISPLAY_Y_POS_TX_FREQ + (FONT_SIZE_3_HEIGHT / 2) - (FONT_SIZE_3_HEIGHT / 4) - 1),
|
|
67 + DISPLAY_H_OFFSET, (DISPLAY_Y_POS_TX_FREQ + (FONT_SIZE_3_HEIGHT / 2) + (FONT_SIZE_3_HEIGHT / 4) - 1)
|
|
}, // Up
|
|
{
|
|
59 + DISPLAY_H_OFFSET, (DISPLAY_Y_POS_TX_FREQ + (FONT_SIZE_3_HEIGHT / 2) + (FONT_SIZE_3_HEIGHT / 4) - 1),
|
|
59 + DISPLAY_H_OFFSET, (DISPLAY_Y_POS_TX_FREQ + (FONT_SIZE_3_HEIGHT / 2) - (FONT_SIZE_3_HEIGHT / 4) - 1),
|
|
67 + DISPLAY_H_OFFSET, (DISPLAY_Y_POS_TX_FREQ + (FONT_SIZE_3_HEIGHT / 2) - 1)
|
|
}
|
|
};
|
|
|
|
displayFillTriangle(scanDirArrow[(uiDataGlobal.Scan.direction > 0)][0], scanDirArrow[(uiDataGlobal.Scan.direction > 0)][1],
|
|
scanDirArrow[(uiDataGlobal.Scan.direction > 0)][2], scanDirArrow[(uiDataGlobal.Scan.direction > 0)][3],
|
|
scanDirArrow[(uiDataGlobal.Scan.direction > 0)][4], scanDirArrow[(uiDataGlobal.Scan.direction > 0)][5], true);
|
|
}
|
|
}
|
|
else // Entering digits
|
|
{
|
|
int16_t xCursor = -1;
|
|
int16_t yCursor = -1;
|
|
#if defined(HAS_COLOURS)
|
|
bool dblHeight = settingsIsOptionBitSet(BIT_UI_USES_DOUBLE_HEIGHT);
|
|
#else
|
|
const bool dblHeight = false;
|
|
#endif
|
|
|
|
if ((screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_NORMAL) ||
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_DUAL_SCAN))
|
|
{
|
|
snprintf(buffer, SCREEN_LINE_BUFFER_SIZE,
|
|
#if defined(PLATFORM_RD5R)
|
|
"%c%c%c.%c%c%c%c%c",
|
|
#else
|
|
"%c%c%c.%c%c%c%c%c MHz",
|
|
#endif
|
|
uiDataGlobal.FreqEnter.digits[0], uiDataGlobal.FreqEnter.digits[1], uiDataGlobal.FreqEnter.digits[2],
|
|
uiDataGlobal.FreqEnter.digits[3], uiDataGlobal.FreqEnter.digits[4], uiDataGlobal.FreqEnter.digits[5], uiDataGlobal.FreqEnter.digits[6], uiDataGlobal.FreqEnter.digits[7]);
|
|
|
|
displayPrintCenteredDoubleHeight((dblHeight ? DISPLAY_Y_POS_RX_FREQ : ((selectedFreq == VFO_SELECTED_FREQUENCY_INPUT_TX) ? DISPLAY_Y_POS_TX_FREQ : DISPLAY_Y_POS_RX_FREQ)), buffer, FONT_SIZE_3, dblHeight);
|
|
|
|
// Cursor
|
|
if (uiDataGlobal.FreqEnter.index < 8)
|
|
{
|
|
xCursor = ((DISPLAY_SIZE_X - (strlen(buffer) * 8)) >> 1) + ((uiDataGlobal.FreqEnter.index + ((uiDataGlobal.FreqEnter.index > 2) ? 1 : 0)) * 8);
|
|
yCursor = (dblHeight ?
|
|
DISPLAY_Y_POS_RX_FREQ :
|
|
((selectedFreq == VFO_SELECTED_FREQUENCY_INPUT_TX) ? DISPLAY_Y_POS_TX_FREQ : DISPLAY_Y_POS_RX_FREQ))
|
|
+ ((FONT_SIZE_3_HEIGHT * (dblHeight ? 2 : 1)) - 2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint16_t hiX = DISPLAY_SIZE_X - ((7 * 8) + 2) - (DISPLAY_H_OFFSET / 2);
|
|
int labelsVOffset =
|
|
#if defined(PLATFORM_RD5R)
|
|
4;
|
|
#else
|
|
0;
|
|
#endif
|
|
#if defined(PLATFORM_VARIANT_DM1701)
|
|
uint16_t dm1701VOffset = (dblHeight ? 8 : 0);
|
|
#else
|
|
const uint16_t dm1701VOffset = 0;
|
|
#endif
|
|
|
|
#if defined(HAS_COLOURS)
|
|
if (dblHeight)
|
|
{
|
|
labelsVOffset += (FONT_SIZE_3_HEIGHT / 2);
|
|
}
|
|
#endif
|
|
|
|
displayPrintAt(5 + (DISPLAY_H_OFFSET / 2), DISPLAY_Y_POS_RX_FREQ - labelsVOffset - dm1701VOffset, currentLanguage->low, FONT_SIZE_3);
|
|
displayDrawFastVLine(0 + (DISPLAY_H_OFFSET / 2), DISPLAY_Y_POS_RX_FREQ - labelsVOffset - dm1701VOffset, DISPLAY_SIZE_Y - (DISPLAY_Y_POS_RX_FREQ - labelsVOffset), true);
|
|
displayDrawFastHLine(1 + (DISPLAY_H_OFFSET / 2), DISPLAY_Y_POS_TX_FREQ - (labelsVOffset / 2) - dm1701VOffset, 57, true);
|
|
|
|
sprintf(buffer, "%c%c%c.%c%c%c", uiDataGlobal.FreqEnter.digits[0], uiDataGlobal.FreqEnter.digits[1], uiDataGlobal.FreqEnter.digits[2],
|
|
uiDataGlobal.FreqEnter.digits[3], uiDataGlobal.FreqEnter.digits[4], uiDataGlobal.FreqEnter.digits[5]);
|
|
|
|
displayPrintAtDoubleHeight(2 + (DISPLAY_H_OFFSET / 2), DISPLAY_Y_POS_TX_FREQ + (DISPLAY_V_EXTRA_PIXELS / 8) - (dblHeight ? (FONT_SIZE_3_HEIGHT / 2) : 0) - dm1701VOffset, buffer, FONT_SIZE_3, dblHeight);
|
|
|
|
displayPrintAt(73 + ((DISPLAY_H_OFFSET / 2) * 3), DISPLAY_Y_POS_RX_FREQ - labelsVOffset - dm1701VOffset, currentLanguage->high, FONT_SIZE_3);
|
|
displayDrawFastVLine(68 + ((DISPLAY_H_OFFSET / 2) * 3), DISPLAY_Y_POS_RX_FREQ - labelsVOffset - dm1701VOffset, DISPLAY_SIZE_Y - (DISPLAY_Y_POS_RX_FREQ - labelsVOffset), true);
|
|
displayDrawFastHLine(69 + ((DISPLAY_H_OFFSET / 2) * 3), DISPLAY_Y_POS_TX_FREQ - (labelsVOffset / 2) - dm1701VOffset, 57, true);
|
|
|
|
sprintf(buffer, "%c%c%c.%c%c%c", uiDataGlobal.FreqEnter.digits[6], uiDataGlobal.FreqEnter.digits[7], uiDataGlobal.FreqEnter.digits[8],
|
|
uiDataGlobal.FreqEnter.digits[9], uiDataGlobal.FreqEnter.digits[10], uiDataGlobal.FreqEnter.digits[11]);
|
|
|
|
displayPrintAtDoubleHeight(hiX, DISPLAY_Y_POS_TX_FREQ + (DISPLAY_V_EXTRA_PIXELS / 8) - (dblHeight ? (FONT_SIZE_3_HEIGHT / 2) : 0) - dm1701VOffset, buffer, FONT_SIZE_3, dblHeight);
|
|
|
|
// Cursor
|
|
if (uiDataGlobal.FreqEnter.index < FREQ_ENTER_DIGITS_MAX)
|
|
{
|
|
xCursor = ((uiDataGlobal.FreqEnter.index < 6) ? 10 + (DISPLAY_H_OFFSET / 2) : hiX) // X start
|
|
+ (((uiDataGlobal.FreqEnter.index < 6) ? (uiDataGlobal.FreqEnter.index - 1) : (uiDataGlobal.FreqEnter.index - 7)) * 8) // Length
|
|
+ ((uiDataGlobal.FreqEnter.index > 2 ? (uiDataGlobal.FreqEnter.index > 8 ? 2 : 1) : 0) * 8); // MHz/kHz separator(s)
|
|
|
|
yCursor = DISPLAY_Y_POS_TX_FREQ + (FONT_SIZE_3_HEIGHT - 2) + (DISPLAY_V_EXTRA_PIXELS / 8) + (dblHeight ? (FONT_SIZE_3_HEIGHT / 2) : 0) - dm1701VOffset;
|
|
}
|
|
}
|
|
|
|
if ((xCursor >= 0) && (yCursor >= 0))
|
|
{
|
|
displayDrawFastHLine(xCursor + 1, yCursor, 6, blink);
|
|
|
|
if ((ticksGetMillis() - blinkTime) > 500)
|
|
{
|
|
blinkTime = ticksGetMillis();
|
|
blink = !blink;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
displayRender();
|
|
break;
|
|
|
|
case QSO_DISPLAY_CALLER_DATA:
|
|
case QSO_DISPLAY_CALLER_DATA_UPDATE:
|
|
uiDataGlobal.displayQSOStatePrev = QSO_DISPLAY_CALLER_DATA;
|
|
uiDataGlobal.isDisplayingQSOData = true;
|
|
uiDataGlobal.displayChannelSettings = false;
|
|
uiUtilityRenderQSOData();
|
|
displayRender();
|
|
break;
|
|
|
|
case QSO_DISPLAY_IDLE:
|
|
break;
|
|
}
|
|
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_IDLE;
|
|
}
|
|
|
|
bool uiVFOModeIsTXFocused(void)
|
|
{
|
|
return (selectedFreq == VFO_SELECTED_FREQUENCY_INPUT_TX);
|
|
}
|
|
|
|
void uiVFOModeStopScanning(void)
|
|
{
|
|
bool resetAPRS = true;
|
|
|
|
if (uiDataGlobal.Scan.toneActive)
|
|
{
|
|
if (prevCSSTone != (CODEPLUG_CSS_TONE_NONE - 1))
|
|
{
|
|
currentChannelData->rxTone = prevCSSTone;
|
|
prevCSSTone = (CODEPLUG_CSS_TONE_NONE - 1);
|
|
}
|
|
|
|
trxSetRxCSS(RADIO_DEVICE_PRIMARY, currentChannelData->rxTone);
|
|
uiDataGlobal.Scan.toneActive = false;
|
|
trxSetAnalogFilterLevel(nonVolatileSettings.analogFilterLevel);// Restore the filter setting after the tone scan
|
|
resetAPRS = false;
|
|
}
|
|
|
|
uiDataGlobal.Scan.active = false;
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_DUAL_SCAN)
|
|
{
|
|
screenOperationMode[CHANNEL_VFO_A] = screenOperationMode[CHANNEL_VFO_B] = VFO_SCREEN_OPERATION_NORMAL;
|
|
settingsSet(nonVolatileSettings.currentVFONumber, nonVolatileSettings.currentVFONumber);
|
|
|
|
rxPowerSavingSetLevel(nonVolatileSettings.ecoLevel);// Level is reduced by 1 when Dual Watch , so re-instate it back to the correct setting
|
|
}
|
|
else if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SWEEP)
|
|
{
|
|
screenOperationMode[nonVolatileSettings.currentVFONumber] = VFO_SCREEN_OPERATION_NORMAL;
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
HRC6000ClearColorCodeSynchronisation();
|
|
}
|
|
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_OR_VFO_FREQ, PROMPT_THRESHOLD_3);
|
|
uiVFOModeUpdateScreen(0); // Needs to redraw the screen now
|
|
|
|
if (resetAPRS)
|
|
{
|
|
aprsBeaconingResetTimers();
|
|
}
|
|
}
|
|
|
|
static void updateFrequency(int frequency, bool announceImmediately)
|
|
{
|
|
if (selectedFreq == VFO_SELECTED_FREQUENCY_INPUT_TX)
|
|
{
|
|
if (trxGetBandFromFrequency(frequency) != FREQUENCY_OUT_OF_BAND)
|
|
{
|
|
currentChannelData->txFreq = frequency;
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
soundSetMelody(MELODY_ACK_BEEP);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int deltaFrequency = frequency - currentChannelData->rxFreq;
|
|
if (trxGetBandFromFrequency(frequency) != FREQUENCY_OUT_OF_BAND)
|
|
{
|
|
currentChannelData->rxFreq = frequency;
|
|
currentChannelData->txFreq = currentChannelData->txFreq + deltaFrequency;
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
|
|
if (trxGetBandFromFrequency(currentChannelData->txFreq) != FREQUENCY_OUT_OF_BAND)
|
|
{
|
|
soundSetMelody(MELODY_ACK_BEEP);
|
|
}
|
|
else
|
|
{
|
|
currentChannelData->txFreq = frequency;
|
|
soundSetMelody(MELODY_ERROR_BEEP);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
soundSetMelody(MELODY_ERROR_BEEP);
|
|
}
|
|
}
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_OR_VFO_FREQ, announceImmediately);
|
|
|
|
menuPrivateCallClear();
|
|
settingsSetVFODirty();
|
|
}
|
|
|
|
void uiVFOModeLoadChannelData(bool forceAPRSReset)
|
|
{
|
|
trxSetModeAndBandwidth(currentChannelData->chMode, (codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_BW_25K) != 0));
|
|
|
|
if (currentChannelData->chMode == RADIO_MODE_ANALOG)
|
|
{
|
|
CodeplugAPRSConfig_t aprsConfig;
|
|
bool aprsConfigUnsetOrQSY = (currentChannelData->aprsConfigIndex == 0) ||
|
|
(codeplugAPRSGetDataForIndex(currentChannelData->aprsConfigIndex, &aprsConfig) && (aprsConfig.txFrequency != 0U));
|
|
|
|
if (aprsConfigUnsetOrQSY == false)
|
|
{
|
|
currentChannelData->rxTone = CODEPLUG_CSS_TONE_NONE;
|
|
currentChannelData->txTone = CODEPLUG_CSS_TONE_NONE;
|
|
codeplugChannelSetFlag(currentChannelData, CHANNEL_FLAG_VOX, 0);
|
|
}
|
|
|
|
if (!uiDataGlobal.Scan.toneActive)
|
|
{
|
|
trxSetRxCSS(RADIO_DEVICE_PRIMARY, currentChannelData->rxTone);
|
|
}
|
|
|
|
if (uiDataGlobal.Scan.active == false)
|
|
{
|
|
uiDataGlobal.Scan.state = SCAN_STATE_SCANNING;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint32_t channelDMRId = codeplugChannelGetOptionalDMRID(currentChannelData);
|
|
|
|
if (uiDataGlobal.manualOverrideDMRId == 0)
|
|
{
|
|
if (channelDMRId == 0)
|
|
{
|
|
trxDMRID = uiDataGlobal.userDMRId;
|
|
}
|
|
else
|
|
{
|
|
trxDMRID = channelDMRId;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
trxDMRID = uiDataGlobal.manualOverrideDMRId;
|
|
}
|
|
|
|
// Set CC when:
|
|
// - scanning
|
|
// - CC Filter is ON
|
|
// - CC Filter is OFF but not held anymore or loading a new channel (this avoids restoring Channel's CC when releasing the PTT key, or getting out of menus)
|
|
if (uiDataGlobal.Scan.active ||
|
|
((nonVolatileSettings.dmrCcTsFilter & DMR_CC_FILTER_PATTERN) ||
|
|
(((nonVolatileSettings.dmrCcTsFilter & DMR_CC_FILTER_PATTERN) == 0)
|
|
&& ((HRC6000CCIsHeld() == false) || (previousVFONumber != nonVolatileSettings.currentVFONumber)))))
|
|
{
|
|
trxSetDMRColourCode(currentChannelData->txColor);
|
|
HRC6000ClearColorCodeSynchronisation();
|
|
}
|
|
|
|
if (nonVolatileSettings.overrideTG == 0)
|
|
{
|
|
uiVFOLoadContact(¤tContactData);
|
|
|
|
// Check whether the contact data seems valid
|
|
if ((currentContactData.name[0] == 0) || (currentContactData.tgNumber == 0) || (currentContactData.tgNumber > 9999999))
|
|
{
|
|
settingsSet(nonVolatileSettings.overrideTG, 9);// If the VFO does not have an Rx Group list assigned to it. We can't get a TG from the codeplug. So use TG 9.
|
|
trxTalkGroupOrPcId = nonVolatileSettings.overrideTG;
|
|
trxSetDMRTimeSlot(codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_TIMESLOT_TWO), true);
|
|
tsSetContactHasBeenOverriden(((Channel_t)nonVolatileSettings.currentVFONumber), false);
|
|
}
|
|
else
|
|
{
|
|
trxTalkGroupOrPcId = codeplugContactGetPackedId(¤tContactData);
|
|
trxUpdateTsForCurrentChannelWithSpecifiedContact(¤tContactData);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int manTS = tsGetManualOverrideFromCurrentChannel();
|
|
|
|
trxTalkGroupOrPcId = nonVolatileSettings.overrideTG;
|
|
trxSetDMRTimeSlot((manTS ? (manTS - 1) : codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_TIMESLOT_TWO)), true);
|
|
}
|
|
}
|
|
|
|
if (uiDataGlobal.Scan.active == false)
|
|
{
|
|
if (forceAPRSReset || ((previousVFONumber != nonVolatileSettings.currentVFONumber) && (currentChannelData->chMode == RADIO_MODE_ANALOG)))
|
|
{
|
|
aprsBeaconingResetTimers();
|
|
}
|
|
}
|
|
|
|
previousVFONumber = nonVolatileSettings.currentVFONumber;
|
|
}
|
|
|
|
static void checkAndFixIndexInRxGroup(void)
|
|
{
|
|
if ((currentRxGroupData.NOT_IN_CODEPLUG_numTGsInGroup > 0) &&
|
|
(nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber] > (currentRxGroupData.NOT_IN_CODEPLUG_numTGsInGroup - 1)))
|
|
{
|
|
settingsSet(nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber], 0);
|
|
}
|
|
}
|
|
|
|
void uiVFOLoadContact(CodeplugContact_t *contact)
|
|
{
|
|
// Check if this channel has an Rx Group
|
|
if ((currentRxGroupData.name[0] != 0) && (nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber] < currentRxGroupData.NOT_IN_CODEPLUG_numTGsInGroup))
|
|
{
|
|
codeplugContactGetDataForIndex(currentRxGroupData.contacts[nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber]], contact);
|
|
}
|
|
else
|
|
{
|
|
/* 2020.10.27 vk3kyy. The Contact should not be forced to none just because the Rx group list is none
|
|
if (currentRxGroupData.NOT_IN_CODEPLUG_numTGsInGroup == 0)
|
|
{
|
|
currentChannelData->contact = 0;
|
|
}*/
|
|
|
|
codeplugContactGetDataForIndex(currentChannelData->contact, contact);
|
|
}
|
|
}
|
|
|
|
static void toggleAnalogBandwidth(void)
|
|
{
|
|
uint8_t bw25k = codeplugChannelSetFlag(currentChannelData, CHANNEL_FLAG_BW_25K, !(codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_BW_25K)));
|
|
|
|
if (bw25k)
|
|
{
|
|
nextKeyBeepMelody = (int16_t *)MELODY_KEY_BEEP_FIRST_ITEM;
|
|
}
|
|
|
|
// ToDo announce VP for bandwidth perhaps
|
|
trxSetModeAndBandwidth(RADIO_MODE_ANALOG, (bw25k != 0));
|
|
}
|
|
|
|
static void handleEvent(uiEvent_t *ev)
|
|
{
|
|
if (uiDataGlobal.Scan.active && (ev->events & KEY_EVENT))
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2) == 0)
|
|
{
|
|
// Right key sets the current frequency as a 'nuisance' frequency.
|
|
if((uiDataGlobal.Scan.state == SCAN_STATE_PAUSED) &&
|
|
#if defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380)
|
|
(ev->keys.key == KEY_STAR)
|
|
#else
|
|
(ev->keys.key == KEY_RIGHT)
|
|
#endif
|
|
&&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_DUAL_SCAN)
|
|
&& (screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP))
|
|
{
|
|
uiDataGlobal.Scan.nuisanceDelete[uiDataGlobal.Scan.nuisanceDeleteIndex] = currentChannelData->rxFreq;
|
|
uiDataGlobal.Scan.nuisanceDeleteIndex = (uiDataGlobal.Scan.nuisanceDeleteIndex + 1) % MAX_ZONE_SCAN_NUISANCE_CHANNELS;
|
|
uiDataGlobal.Scan.timer.timeout = SCAN_SKIP_CHANNEL_INTERVAL;//force scan to continue;
|
|
uiDataGlobal.Scan.state = SCAN_STATE_SCANNING;
|
|
keyboardReset();
|
|
return;
|
|
}
|
|
|
|
// Left key reverses the scan direction
|
|
if (
|
|
#if defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380)
|
|
(ev->keys.key == KEY_FRONT_DOWN)
|
|
#else
|
|
(ev->keys.key == KEY_LEFT)
|
|
#endif
|
|
&&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_DUAL_SCAN)
|
|
&& (screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP))
|
|
{
|
|
uiDataGlobal.Scan.direction *= -1;
|
|
keyboardReset();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Stop the scan on any key except UP/ROTARY_INC without SK2 (allows scan to be manually continued)
|
|
// or SK2 on its own (allows Backlight to be triggered)
|
|
if (
|
|
#if defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
(((
|
|
(ev->keys.key == KEY_FRONT_UP)
|
|
#if defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
|| (ev->keys.key == KEY_ROTARY_INCREMENT)
|
|
#endif
|
|
) && (BUTTONCHECK_DOWN(ev, BUTTON_SK2) == 0) &&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SCAN)) == false)
|
|
&&
|
|
((((ev->keys.key == KEY_ROTARY_INCREMENT) || (ev->keys.key == KEY_ROTARY_DECREMENT) || (ev->keys.key == KEY_FRONT_UP) || (ev->keys.key == KEY_FRONT_DOWN)
|
|
#if defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
|| (ev->keys.key == KEY_LEFT) || (ev->keys.key == KEY_RIGHT) || (ev->keys.key == KEY_UP) || (ev->keys.key == KEY_DOWN)
|
|
#endif
|
|
|| (ev->keys.key == KEY_STAR)) &&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SWEEP)) == false)
|
|
|
|
#else // NOT STM32 PLATFORMS, a.k.a MK22 HTs, and MD9600
|
|
(((ev->keys.key == KEY_UP) && (BUTTONCHECK_DOWN(ev, BUTTON_SK2) == 0) &&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SCAN)) == false)
|
|
&&
|
|
((((ev->keys.key == KEY_LEFT) || (ev->keys.key == KEY_RIGHT) || (ev->keys.key == KEY_UP) || (ev->keys.key == KEY_DOWN) || (ev->keys.key == KEY_STAR)) &&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SWEEP)) == false)
|
|
#endif
|
|
)
|
|
{
|
|
uiVFOModeStopScanning();
|
|
keyboardReset();
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_OR_VFO_FREQ, PROMPT_THRESHOLD_3);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ev->events & FUNCTION_EVENT)
|
|
{
|
|
if (ev->function == FUNC_START_SCANNING)
|
|
{
|
|
scanInit();
|
|
setCurrentFreqToScanLimits();
|
|
uiDataGlobal.Scan.active = true;
|
|
return;
|
|
}
|
|
else if (ev->function == FUNC_REDRAW)
|
|
{
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiVFOModeUpdateScreen(0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (handleMonitorMode(ev))
|
|
{
|
|
uiDataGlobal.displayChannelSettings = false;
|
|
uiDataGlobal.reverseRepeaterVFO = false;
|
|
return;
|
|
}
|
|
|
|
if (ev->events & BUTTON_EVENT)
|
|
{
|
|
|
|
#if ! defined(PLATFORM_RD5R)
|
|
// Stop the scan if any button is pressed.
|
|
if (uiDataGlobal.Scan.active && BUTTONCHECK_DOWN(ev, BUTTON_ORANGE))
|
|
{
|
|
uiVFOModeStopScanning();
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
if (rebuildVoicePromptOnExtraLongSK1(ev))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (repeatVoicePromptOnSK1(ev))
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint32_t tg = (LinkHead->talkGroupOrPcId & 0xFFFFFF);
|
|
|
|
// If Blue button is pressed during reception it sets the Tx TG to the incoming TG
|
|
if (uiDataGlobal.isDisplayingQSOData && BUTTONCHECK_SHORTUP(ev, BUTTON_SK2) && (trxGetMode() == RADIO_MODE_DIGITAL) &&
|
|
((trxTalkGroupOrPcId != tg) ||
|
|
((dmrMonitorCapturedTS != -1) && (dmrMonitorCapturedTS != trxGetDMRTimeSlot())) ||
|
|
(trxGetDMRColourCode() != currentChannelData->txColor)))
|
|
{
|
|
lastHeardClearLastID();
|
|
|
|
// Set TS to overriden TS
|
|
if ((dmrMonitorCapturedTS != -1) && (dmrMonitorCapturedTS != trxGetDMRTimeSlot()))
|
|
{
|
|
trxSetDMRTimeSlot(dmrMonitorCapturedTS, false);
|
|
tsSetManualOverride(((Channel_t)nonVolatileSettings.currentVFONumber), (dmrMonitorCapturedTS + 1));
|
|
}
|
|
|
|
if (trxTalkGroupOrPcId != tg)
|
|
{
|
|
trxTalkGroupOrPcId = tg;
|
|
settingsSet(nonVolatileSettings.overrideTG, trxTalkGroupOrPcId);
|
|
}
|
|
|
|
currentChannelData->txColor = trxGetDMRColourCode();// Set the CC to the current CC, which may have been determined by the CC finding algorithm in C6000.c
|
|
|
|
announceItem(PROMPT_SEQUENCE_CONTACT_TG_OR_PC,PROMPT_THRESHOLD_NEVER_PLAY_IMMEDIATELY);
|
|
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiVFOModeUpdateScreen(0);
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_CALLER_DATA_UPDATE;
|
|
soundSetMelody(MELODY_ACK_BEEP);
|
|
return;
|
|
}
|
|
|
|
// SK2 short tap toggles audio mute. The DMR TG-set handler above consumes the
|
|
// tap (and returns) only while a QSO is on screen, so this won't fire then.
|
|
if (BUTTONCHECK_SHORTUP(ev, BUTTON_SK2) && (BUTTONCHECK_DOWN(ev, BUTTON_SK1) == 0) && (monitorModeData.isEnabled == false))
|
|
{
|
|
toggleAudioMute();
|
|
uiVFOModeUpdateScreen(0);
|
|
return;
|
|
}
|
|
|
|
if ((uiVFOModeSweepScanning(true) == false) && (monitorModeData.isEnabled == false) && (uiDataGlobal.reverseRepeaterVFO == false) && (BUTTONCHECK_DOWN(ev, BUTTON_SK1) && BUTTONCHECK_DOWN(ev, BUTTON_SK2)))
|
|
{
|
|
int prevQSODisp = -1;
|
|
|
|
trxSetFrequency(currentChannelData->txFreq, currentChannelData->rxFreq, DMR_MODE_DMO);// Swap Tx and Rx freqs but force DMR Active mode
|
|
uiDataGlobal.reverseRepeaterVFO = true;
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
|
|
if (uiDataGlobal.displayChannelSettings == false)
|
|
{
|
|
prevQSODisp = uiDataGlobal.displayQSOStatePrev;
|
|
uiDataGlobal.displayChannelSettings = true;
|
|
headerRowIsDirty = true;
|
|
}
|
|
|
|
uiVFOModeUpdateScreen(0);
|
|
|
|
if (prevQSODisp != -1)
|
|
{
|
|
uiDataGlobal.displayQSOStatePrev = prevQSODisp;
|
|
}
|
|
return;
|
|
}
|
|
else if (uiDataGlobal.reverseRepeaterVFO && ((BUTTONCHECK_DOWN(ev, BUTTON_SK1) == 0) || (BUTTONCHECK_DOWN(ev, BUTTON_SK2) == 0)))
|
|
{
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
uiDataGlobal.reverseRepeaterVFO = false;
|
|
|
|
// We are still displaying channel details (SK1 has been released), force to update the screen
|
|
if (uiDataGlobal.displayChannelSettings && (BUTTONCHECK_DOWN(ev, BUTTON_SK1) == 0))
|
|
{
|
|
uiDataGlobal.displayChannelSettings = false;
|
|
}
|
|
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiVFOModeUpdateScreen(0);
|
|
return;
|
|
}
|
|
// Display channel settings (CTCSS, Squelch) while SK1 is pressed
|
|
else if ((uiVFOModeSweepScanning(true) == false) && (monitorModeData.isEnabled == false) && (uiDataGlobal.displayChannelSettings == false) && BUTTONCHECK_DOWN(ev, BUTTON_SK1))
|
|
{
|
|
int prevQSODisp = uiDataGlobal.displayQSOStatePrev;
|
|
|
|
uiDataGlobal.displayChannelSettings = true;
|
|
headerRowIsDirty = true;
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiVFOModeUpdateScreen(0);
|
|
uiDataGlobal.displayQSOStatePrev = prevQSODisp;
|
|
return;
|
|
}
|
|
else if (uiDataGlobal.displayChannelSettings && BUTTONCHECK_DOWN(ev, BUTTON_SK1) == 0)
|
|
{
|
|
uiDataGlobal.displayChannelSettings = false;
|
|
uiDataGlobal.displayQSOState = uiDataGlobal.displayQSOStatePrev;
|
|
|
|
// Maybe QSO State has been overridden, double check if we could now
|
|
// display QSO Data
|
|
if (uiDataGlobal.displayQSOState == QSO_DISPLAY_DEFAULT_SCREEN)
|
|
{
|
|
if (isQSODataAvailableForCurrentTalker())
|
|
{
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_CALLER_DATA;
|
|
}
|
|
}
|
|
|
|
// Leaving Channel Details disable reverse repeater feature
|
|
if (uiDataGlobal.reverseRepeaterVFO)
|
|
{
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
uiDataGlobal.reverseRepeaterVFO = false;
|
|
}
|
|
|
|
uiVFOModeUpdateScreen(0);
|
|
return;
|
|
}
|
|
|
|
#if !defined(PLATFORM_RD5R)
|
|
if (BUTTONCHECK_SHORTUP(ev, BUTTON_ORANGE))
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
announceItem(PROMPT_SEQUENCE_BATTERY, AUDIO_PROMPT_MODE_VOICE_LEVEL_1);
|
|
}
|
|
else
|
|
{
|
|
menuSystemPushNewMenu(UI_VFO_QUICK_MENU);
|
|
|
|
// Trick to beep (AudioAssist), since ORANGE button doesn't produce any beep event
|
|
ev->keys.event |= KEY_MOD_UP;
|
|
ev->keys.key = 127;
|
|
menuVFOExitStatus |= (MENU_STATUS_LIST_TYPE | MENU_STATUS_FORCE_FIRST);
|
|
// End Trick
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (ev->events & KEY_EVENT)
|
|
{
|
|
int keyval = 99;
|
|
|
|
if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN))
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
menuSystemPushNewMenu(MENU_CHANNEL_DETAILS);
|
|
freqEnterReset();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if (uiDataGlobal.FreqEnter.index == 0)
|
|
{
|
|
menuSystemPushNewMenu(MENU_MAIN_MENU);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (uiDataGlobal.FreqEnter.index == 0)
|
|
{
|
|
#if defined(PLATFORM_MD9600)
|
|
if (KEYCHECK_LONGDOWN(ev->keys, KEY_GREEN))
|
|
{
|
|
if (uiDataGlobal.Scan.active)
|
|
{
|
|
uiVFOModeStopScanning();
|
|
}
|
|
|
|
menuSystemPushNewMenu(UI_VFO_QUICK_MENU);
|
|
|
|
// Trick to beep (AudioAssist), since ORANGE button doesn't produce any beep event
|
|
ev->keys.event |= KEY_MOD_UP;
|
|
ev->keys.key = 127;
|
|
menuVFOExitStatus |= (MENU_STATUS_LIST_TYPE | MENU_STATUS_FORCE_FIRST);
|
|
// End Trick
|
|
return;
|
|
}
|
|
else
|
|
#endif
|
|
if (KEYCHECK_LONGDOWN(ev->keys, KEY_HASH) && (KEYCHECK_LONGDOWN_REPEAT(ev->keys, KEY_HASH) == false))
|
|
{
|
|
if (uiDataGlobal.Scan.active && (screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP))
|
|
{
|
|
uiVFOModeStopScanning();
|
|
}
|
|
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP)
|
|
{
|
|
sweepScanInit();
|
|
soundSetMelody(MELODY_KEY_LONG_BEEP);
|
|
}
|
|
return;
|
|
}
|
|
else if (KEYCHECK_SHORTUP(ev->keys, KEY_HASH))
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
menuSystemPushNewMenu(MENU_CONTACT_QUICKLIST);
|
|
}
|
|
else
|
|
{
|
|
menuSystemPushNewMenu(MENU_NUMERICAL_ENTRY);
|
|
}
|
|
return;
|
|
}
|
|
else if (KEYCHECK_SHORTUP(ev->keys, KEY_STAR))
|
|
{
|
|
if (uiVFOModeSweepScanning(true) == false)
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
if (trxGetMode() == RADIO_MODE_ANALOG)
|
|
{
|
|
currentChannelData->chMode = RADIO_MODE_DIGITAL;
|
|
checkAndFixIndexInRxGroup();
|
|
uiVFOModeLoadChannelData(true);
|
|
updateTrxID();
|
|
|
|
menuVFOExitStatus |= MENU_STATUS_FORCE_FIRST;
|
|
}
|
|
else
|
|
{
|
|
currentChannelData->chMode = RADIO_MODE_ANALOG;
|
|
trxSetModeAndBandwidth(currentChannelData->chMode, (codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_BW_25K) != 0));
|
|
}
|
|
|
|
announceItem(PROMPT_SEQUENCE_MODE, PROMPT_THRESHOLD_1);
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
}
|
|
else
|
|
{
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
// Toggle TimeSlot
|
|
trxSetDMRTimeSlot(1 - trxGetDMRTimeSlot(), true);
|
|
tsSetManualOverride(((Channel_t)nonVolatileSettings.currentVFONumber), (trxGetDMRTimeSlot() + 1));
|
|
|
|
if ((nonVolatileSettings.overrideTG == 0) && (currentContactData.reserve1 & CODEPLUG_CONTACT_FLAG_NO_TS_OVERRIDE) == 0x00)
|
|
{
|
|
tsSetContactHasBeenOverriden(((Channel_t)nonVolatileSettings.currentVFONumber), true);
|
|
}
|
|
|
|
audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
|
|
lastHeardClearLastID();
|
|
uiDataGlobal.displayQSOState = uiDataGlobal.displayQSOStatePrev;
|
|
uiVFOModeUpdateScreen(0);
|
|
|
|
if (trxGetDMRTimeSlot() == 0)
|
|
{
|
|
menuVFOExitStatus |= MENU_STATUS_FORCE_FIRST;
|
|
}
|
|
announceItem(PROMPT_SEQUENCE_TS,PROMPT_THRESHOLD_3);
|
|
}
|
|
else
|
|
{
|
|
toggleAnalogBandwidth();
|
|
uiDataGlobal.displayQSOState = uiDataGlobal.displayQSOStatePrev;
|
|
headerRowIsDirty = true;
|
|
uiVFOModeUpdateScreen(0);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (trxGetMode() == RADIO_MODE_ANALOG)
|
|
{
|
|
toggleAnalogBandwidth();
|
|
}
|
|
}
|
|
}
|
|
else if ((uiVFOModeSweepScanning(true) == false) && KEYCHECK_LONGDOWN(ev->keys, KEY_STAR))
|
|
{
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
tsSetManualOverride(((Channel_t)nonVolatileSettings.currentVFONumber), TS_NO_OVERRIDE);
|
|
tsSetContactHasBeenOverriden(((Channel_t)nonVolatileSettings.currentVFONumber), false);
|
|
|
|
// Check if this channel has an Rx Group
|
|
if ((currentRxGroupData.name[0] != 0) &&
|
|
(nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber] < currentRxGroupData.NOT_IN_CODEPLUG_numTGsInGroup))
|
|
{
|
|
codeplugContactGetDataForIndex(currentRxGroupData.contacts[nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber]], ¤tContactData);
|
|
}
|
|
else
|
|
{
|
|
codeplugContactGetDataForIndex(currentChannelData->contact, ¤tContactData);
|
|
}
|
|
|
|
trxUpdateTsForCurrentChannelWithSpecifiedContact(¤tContactData);
|
|
|
|
lastHeardClearLastID();
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiVFOModeUpdateScreen(0);
|
|
announceItem(PROMPT_SEQUENCE_TS, PROMPT_THRESHOLD_1);
|
|
}
|
|
}
|
|
else if(uiVFOModeSweepScanning(true) && // Reset Sweep noise floor or Sweep Gain
|
|
((KEYCHECK_SHORTUP(ev->keys, KEY_DOWN) || KEYCHECK_SHORTUP(ev->keys, KEY_UP)
|
|
#if defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
|| KEYCHECK_SHORTUP(ev->keys, KEY_ROTARY_INCREMENT) || KEYCHECK_SHORTUP(ev->keys, KEY_ROTARY_DECREMENT)
|
|
#if defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
|| KEYCHECK_SHORTUP(ev->keys, KEY_FRONT_UP) || KEYCHECK_SHORTUP(ev->keys, KEY_FRONT_DOWN)
|
|
#endif
|
|
#endif
|
|
)
|
|
&& BUTTONCHECK_DOWN(ev, BUTTON_SK1)))
|
|
{
|
|
vfoSweepRssiNoiseFloor = VFO_SWEEP_RSSI_NOISE_FLOOR_DEFAULT;
|
|
vfoSweepGain = VFO_SWEEP_GAIN_DEFAULT;
|
|
settingsSet(nonVolatileSettings.vfoSweepSettings, ((uiDataGlobal.Scan.sweepStepSizeIndex << 12) | (vfoSweepRssiNoiseFloor << 7) | vfoSweepGain));
|
|
vfoSweepUpdateSamples(0, true, 0);
|
|
}
|
|
else if (
|
|
#if defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
KEYCHECK_SHORTUP(ev->keys, KEY_ROTARY_DECREMENT)
|
|
#if defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
|| KEYCHECK_SHORTUP(ev->keys, KEY_FRONT_DOWN)
|
|
#endif
|
|
#else
|
|
KEYCHECK_SHORTUP(ev->keys, KEY_DOWN) || KEYCHECK_LONGDOWN_REPEAT(ev->keys, KEY_DOWN)
|
|
#endif
|
|
)
|
|
{
|
|
#if defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380)
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2) && uiVFOModeSweepScanning(true))
|
|
{
|
|
setSweepIncDecSetting(SWEEP_SETTING_STEP, false);
|
|
headerRowIsDirty = true;
|
|
}
|
|
else
|
|
{
|
|
handleDownKey(ev);
|
|
}
|
|
return;
|
|
#else
|
|
if (uiVFOModeSweepScanning(true) == false)
|
|
{
|
|
handleDownKey(ev);
|
|
}
|
|
else
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
// Sweep noise floor
|
|
setSweepIncDecSetting(SWEEP_SETTING_RSSI, false);
|
|
}
|
|
else
|
|
{
|
|
// Sweep gain
|
|
setSweepIncDecSetting(SWEEP_SETTING_GAIN, false);
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
else if (
|
|
#if defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
KEYCHECK_LONGDOWN(ev->keys, KEY_FRONT_DOWN)
|
|
#else
|
|
KEYCHECK_LONGDOWN(ev->keys, KEY_DOWN)
|
|
#endif
|
|
)
|
|
{
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SCAN)
|
|
{
|
|
screenOperationMode[nonVolatileSettings.currentVFONumber] = VFO_SCREEN_OPERATION_NORMAL;
|
|
nextKeyBeepMelody = (int16_t *)MELODY_ACK_BEEP;
|
|
uiVFOModeStopScanning();
|
|
return;
|
|
}
|
|
}
|
|
else if (
|
|
#if defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
KEYCHECK_SHORTUP(ev->keys, KEY_ROTARY_INCREMENT)
|
|
#if defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
|| KEYCHECK_SHORTUP(ev->keys, KEY_FRONT_UP)
|
|
#endif
|
|
#else
|
|
KEYCHECK_SHORTUP(ev->keys, KEY_UP) || KEYCHECK_LONGDOWN_REPEAT(ev->keys, KEY_UP)
|
|
#endif
|
|
)
|
|
{
|
|
#if defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380)
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2) && uiVFOModeSweepScanning(true))
|
|
{
|
|
setSweepIncDecSetting(SWEEP_SETTING_STEP, true);
|
|
headerRowIsDirty = true;
|
|
}
|
|
else
|
|
{
|
|
handleUpKey(ev);
|
|
}
|
|
return;
|
|
#else
|
|
if (uiVFOModeSweepScanning(true) == false)
|
|
{
|
|
handleUpKey(ev);
|
|
}
|
|
else
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
// Sweep noise floor
|
|
setSweepIncDecSetting(SWEEP_SETTING_RSSI, true);
|
|
}
|
|
else
|
|
{
|
|
// Sweep gain
|
|
setSweepIncDecSetting(SWEEP_SETTING_GAIN, true);
|
|
}
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
else if (KEYCHECK_LONGDOWN(ev->keys,
|
|
#if defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
KEY_FRONT_UP
|
|
#else
|
|
KEY_UP
|
|
#endif
|
|
) && (BUTTONCHECK_DOWN(ev, BUTTON_SK2) == 0))
|
|
{
|
|
if ((screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SCAN) &&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_DUAL_SCAN) &&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP))
|
|
{
|
|
scanInit();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if ((screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_DUAL_SCAN) &&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP))
|
|
{
|
|
setCurrentFreqToScanLimits();
|
|
if (uiDataGlobal.Scan.active == false)
|
|
{
|
|
// User maybe has change the mode, update.
|
|
// In DIGITAL mode, we need at least 120ms to see the HR-C6000 to start the TS ISR.
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
int dwellTime;
|
|
if(uiDataGlobal.Scan.stepTimeMilliseconds > 150) // if >150ms use DMR Slow mode
|
|
{
|
|
dwellTime = ((currentRadioDevice->trxDMRModeRx == DMR_MODE_DMO) ? SCAN_DMR_SIMPLEX_SLOW_MIN_DWELL_TIME : SCAN_DMR_DUPLEX_SLOW_MIN_DWELL_TIME);
|
|
}
|
|
else
|
|
{
|
|
dwellTime = ((currentRadioDevice->trxDMRModeRx == DMR_MODE_DMO) ? SCAN_DMR_SIMPLEX_FAST_MIN_DWELL_TIME : SCAN_DMR_DUPLEX_FAST_MIN_DWELL_TIME);
|
|
}
|
|
|
|
uiDataGlobal.Scan.dwellTime = ((uiDataGlobal.Scan.stepTimeMilliseconds < dwellTime) ? dwellTime : uiDataGlobal.Scan.stepTimeMilliseconds);
|
|
}
|
|
else
|
|
{
|
|
uiDataGlobal.Scan.dwellTime = uiDataGlobal.Scan.stepTimeMilliseconds;
|
|
}
|
|
|
|
clearNuisance();
|
|
|
|
uiDataGlobal.Scan.active = true;
|
|
if (voicePromptsIsPlaying())
|
|
{
|
|
voicePromptsTerminate();
|
|
}
|
|
soundSetMelody(MELODY_KEY_LONG_BEEP);
|
|
keyboardReset();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if defined(PLATFORM_DM1801)
|
|
else if (KEYCHECK_LONGDOWN(ev->keys, KEY_RED) && (KEYCHECK_LONGDOWN_REPEAT(ev->keys, KEY_RED) == false))
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK1))
|
|
{
|
|
uiChannelModeOrVFOModeThemeDaytimeChange(false, false);
|
|
return;
|
|
}
|
|
}
|
|
else if (KEYCHECK_SHORTUP(ev->keys, KEY_A_B))
|
|
{
|
|
#else
|
|
else if (KEYCHECK_LONGDOWN(ev->keys, KEY_RED) && (KEYCHECK_LONGDOWN_REPEAT(ev->keys, KEY_RED) == false))
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK1))
|
|
{
|
|
uiChannelModeOrVFOModeThemeDaytimeChange(false, false);
|
|
return;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
settingsSet(nonVolatileSettings.currentVFONumber, (1 - nonVolatileSettings.currentVFONumber));// Switch to other VFO
|
|
currentChannelData = &settingsVFOChannel[nonVolatileSettings.currentVFONumber];
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
|
|
menuVFOExitStatus = MENU_STATUS_SUCCESS;
|
|
menuSystemPopAllAndDisplayRootMenu(); // Force to set all TX/RX settings.
|
|
|
|
#if defined(PLATFORM_DM1801)
|
|
if (nonVolatileSettings.currentVFONumber == 0)
|
|
#else
|
|
if (nonVolatileSettings.currentVFONumber == 1) // Yes, inverted here, as the beep will apply to other VFO
|
|
#endif
|
|
{
|
|
menuVFOExitStatus |= MENU_STATUS_FORCE_FIRST;
|
|
}
|
|
else
|
|
{
|
|
menuVFOExitStatus = MENU_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else if (KEYCHECK_SHORTUP(ev->keys, KEY_RED))
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK1))
|
|
{
|
|
uiChannelModeOrVFOModeThemeDaytimeChange(true, false);
|
|
return;
|
|
}
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2) && (uiDataGlobal.tgBeforePcMode != 0))
|
|
{
|
|
settingsSet(nonVolatileSettings.overrideTG, uiDataGlobal.tgBeforePcMode);
|
|
updateTrxID();
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;// Force redraw
|
|
menuPrivateCallClear();
|
|
uiVFOModeUpdateScreen(0);
|
|
return;// The event has been handled
|
|
}
|
|
|
|
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_DM1801A) || defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
if ((trxGetMode() == RADIO_MODE_DIGITAL) && (audioAmpGetStatus() & AUDIO_AMP_CHANNEL_RF))
|
|
{
|
|
HRC6000ClearActiveDMRID();
|
|
}
|
|
menuVFOExitStatus |= MENU_STATUS_FORCE_FIRST;// Audible signal that the Channel screen has been selected
|
|
menuSystemSetCurrentMenu(UI_CHANNEL_MODE);
|
|
aprsBeaconingResetTimers();
|
|
#endif
|
|
return;
|
|
}
|
|
#if defined(PLATFORM_DM1801) || defined(PLATFORM_RD5R)
|
|
else if (KEYCHECK_SHORTUP(ev->keys, KEY_VFO_MR))
|
|
{
|
|
if ((trxGetMode() == RADIO_MODE_DIGITAL) && (audioAmpGetStatus() & AUDIO_AMP_CHANNEL_RF))
|
|
{
|
|
HRC6000ClearActiveDMRID();
|
|
}
|
|
menuVFOExitStatus |= MENU_STATUS_FORCE_FIRST;// Audible signal that the Channel screen has been selected
|
|
menuSystemSetCurrentMenu(UI_CHANNEL_MODE);
|
|
aprsBeaconingResetTimers();
|
|
return;
|
|
}
|
|
#endif
|
|
#if defined(PLATFORM_RD5R)
|
|
else if (KEYCHECK_LONGDOWN(ev->keys, KEY_VFO_MR) && (BUTTONCHECK_DOWN(ev, BUTTON_SK1) == 0))
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
announceItem(PROMPT_SEQUENCE_BATTERY, AUDIO_PROMPT_MODE_VOICE_LEVEL_1);
|
|
}
|
|
else
|
|
{
|
|
menuSystemPushNewMenu(UI_VFO_QUICK_MENU);
|
|
|
|
// Trick to beep (AudioAssist), since ORANGE button doesn't produce any beep event
|
|
ev->keys.event |= KEY_MOD_UP;
|
|
ev->keys.key = 127;
|
|
menuVFOExitStatus |= (MENU_STATUS_LIST_TYPE | MENU_STATUS_FORCE_FIRST);
|
|
// End Trick
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
else if (KEYCHECK_LONGDOWN(ev->keys, KEY_INCREASE) && BUTTONCHECK_DOWN(ev, BUTTON_SK2)) // set as KEY_RIGHT on some platforms + SK2
|
|
{
|
|
// Long press allows the 5W+ power setting to be selected immediately (but not while sweep scanning)
|
|
if ((uiVFOModeSweepScanning(true) == false) && increasePowerLevel(true))
|
|
{
|
|
headerRowIsDirty = true;
|
|
}
|
|
}
|
|
else if (KEYCHECK_SHORTUP(ev->keys, KEY_INCREASE)) // set as KEY_RIGHT on some platforms
|
|
{
|
|
if (uiVFOModeSweepScanning(true))
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
#if defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380)
|
|
// In Sweep scan, Right increase RSSI or GAIN
|
|
{
|
|
setSweepIncDecSetting(SWEEP_SETTING_RSSI, true);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
setSweepIncDecSetting(SWEEP_SETTING_GAIN, true);
|
|
return;
|
|
}
|
|
#else
|
|
// In Sweep scan, Right increase RX freq
|
|
{
|
|
setSweepIncDecSetting(SWEEP_SETTING_STEP, false);
|
|
headerRowIsDirty = true;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
handleUpKey(ev);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
if (increasePowerLevel(false))
|
|
{
|
|
headerRowIsDirty = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380)
|
|
if (uiDataGlobal.Scan.active == false)
|
|
{
|
|
#endif
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
if (currentRxGroupData.NOT_IN_CODEPLUG_numTGsInGroup != 0)
|
|
{
|
|
if (currentRxGroupData.NOT_IN_CODEPLUG_numTGsInGroup > 1)
|
|
{
|
|
if (nonVolatileSettings.overrideTG == 0)
|
|
{
|
|
settingsIncrement(nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber], 1);
|
|
checkAndFixIndexInRxGroup();
|
|
}
|
|
|
|
if (nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber] == 0)
|
|
{
|
|
menuVFOExitStatus |= (MENU_STATUS_LIST_TYPE | MENU_STATUS_FORCE_FIRST);
|
|
}
|
|
}
|
|
settingsSet(nonVolatileSettings.overrideTG, 0);// setting the override TG to 0 indicates the TG is not overridden
|
|
}
|
|
menuPrivateCallClear();
|
|
updateTrxID();
|
|
// We're in digital mode, RXing, and current talker is already at the top of last heard list,
|
|
// hence immediately display complete contact/TG info on screen
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;//(isQSODataAvailableForCurrentTalker() ? QSO_DISPLAY_CALLER_DATA : QSO_DISPLAY_DEFAULT_SCREEN);
|
|
if (isQSODataAvailableForCurrentTalker())
|
|
{
|
|
(void)addTimerCallback(uiUtilityRenderQSODataAndUpdateScreen, 2000, UI_VFO_MODE, true);
|
|
}
|
|
uiVFOModeUpdateScreen(0);
|
|
announceItem(PROMPT_SEQUENCE_CONTACT_TG_OR_PC,PROMPT_THRESHOLD_3);
|
|
}
|
|
else
|
|
{
|
|
if(currentChannelData->sql == 0) //If we were using default squelch level
|
|
{
|
|
currentChannelData->sql = nonVolatileSettings.squelchDefaults[currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND]];//start the adjustment from that point.
|
|
}
|
|
|
|
if (currentChannelData->sql < CODEPLUG_MAX_VARIABLE_SQUELCH)
|
|
{
|
|
currentChannelData->sql++;
|
|
}
|
|
|
|
announceItem(PROMPT_SQUENCE_SQUELCH,PROMPT_THRESHOLD_3);
|
|
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiNotificationShow(NOTIFICATION_TYPE_SQUELCH, NOTIFICATION_ID_SQUELCH, 1000, NULL, false);
|
|
uiVFOModeUpdateScreen(0);
|
|
}
|
|
#if defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380)
|
|
}
|
|
else
|
|
{
|
|
handleUpKey(ev); //Up key while scan paused continues the scan
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else if (KEYCHECK_SHORTUP(ev->keys, KEY_DECREASE)) // set as KEY_LEFT on some platforms
|
|
{
|
|
if (uiVFOModeSweepScanning(true))
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
#if defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380)
|
|
// In Sweep scan, Right decrease RSSI or GAIN
|
|
{
|
|
setSweepIncDecSetting(SWEEP_SETTING_RSSI, false);
|
|
}
|
|
else
|
|
{
|
|
setSweepIncDecSetting(SWEEP_SETTING_GAIN, false);
|
|
}
|
|
return;
|
|
#else
|
|
// In Sweep scan, Left decrease RX freq
|
|
{
|
|
setSweepIncDecSetting(SWEEP_SETTING_STEP, true);
|
|
headerRowIsDirty = true;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
handleDownKey(ev);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
if (decreasePowerLevel())
|
|
{
|
|
headerRowIsDirty = true;
|
|
}
|
|
|
|
if (trxGetPowerLevel() == 0)
|
|
{
|
|
menuVFOExitStatus |= (MENU_STATUS_LIST_TYPE | MENU_STATUS_FORCE_FIRST);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
if (currentRxGroupData.NOT_IN_CODEPLUG_numTGsInGroup != 0)
|
|
{
|
|
if (currentRxGroupData.NOT_IN_CODEPLUG_numTGsInGroup > 1)
|
|
{
|
|
// To Do change TG in on same channel freq
|
|
if (nonVolatileSettings.overrideTG == 0)
|
|
{
|
|
settingsDecrement(nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber], 1);
|
|
if (nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber] < 0)
|
|
{
|
|
settingsSet(nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber],
|
|
(int16_t) (currentRxGroupData.NOT_IN_CODEPLUG_numTGsInGroup - 1));
|
|
}
|
|
|
|
if(nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber] == 0)
|
|
{
|
|
menuVFOExitStatus |= MENU_STATUS_FORCE_FIRST;
|
|
}
|
|
}
|
|
}
|
|
settingsSet(nonVolatileSettings.overrideTG, 0);// setting the override TG to 0 indicates the TG is not overridden
|
|
}
|
|
menuPrivateCallClear();
|
|
updateTrxID();
|
|
// We're in digital mode, RXing, and current talker is already at the top of last heard list,
|
|
// hence immediately display complete contact/TG info on screen
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;//(isQSODataAvailableForCurrentTalker() ? QSO_DISPLAY_CALLER_DATA : QSO_DISPLAY_DEFAULT_SCREEN);
|
|
if (isQSODataAvailableForCurrentTalker())
|
|
{
|
|
(void)addTimerCallback(uiUtilityRenderQSODataAndUpdateScreen, 2000, UI_VFO_MODE, true);
|
|
}
|
|
uiVFOModeUpdateScreen(0);
|
|
announceItem(PROMPT_SEQUENCE_CONTACT_TG_OR_PC,PROMPT_THRESHOLD_3);
|
|
}
|
|
else
|
|
{
|
|
if(currentChannelData->sql == 0) //If we were using default squelch level
|
|
{
|
|
currentChannelData->sql = nonVolatileSettings.squelchDefaults[currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND]];//start the adjustment from that point.
|
|
}
|
|
|
|
if (currentChannelData->sql > CODEPLUG_MIN_VARIABLE_SQUELCH)
|
|
{
|
|
currentChannelData->sql--;
|
|
}
|
|
|
|
announceItem(PROMPT_SQUENCE_SQUELCH,PROMPT_THRESHOLD_3);
|
|
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiNotificationShow(NOTIFICATION_TYPE_SQUELCH, NOTIFICATION_ID_SQUELCH, 1000, NULL, false);
|
|
uiVFOModeUpdateScreen(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (KEYCHECK_LONGDOWN(ev->keys, KEY_0) && (KEYCHECK_LONGDOWN_REPEAT(ev->keys, KEY_0) == false))
|
|
{
|
|
char buf[SCREEN_LINE_BUFFER_SIZE];
|
|
bool muted = audioAmpIsMuted();
|
|
|
|
audioAmpMute(!muted);
|
|
|
|
snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "%s: %s", currentLanguage->mute, (muted ? currentLanguage->no : currentLanguage->yes));
|
|
uiNotificationShow(NOTIFICATION_TYPE_MESSAGE, NOTIFICATION_ID_MESSAGE, 1000U, buf, true);
|
|
|
|
if (muted)
|
|
{
|
|
soundSetMelody(MELODY_KEY_BEEP_FIRST_ITEM);
|
|
nextKeyBeepMelody = NULL;
|
|
ev->keys.event &= ~(KEY_MOD_LONG | KEY_MOD_DOWN | KEY_MOD_PRESS | KEY_MOD_UP);
|
|
ev->keys.key = 0;
|
|
|
|
voicePromptsInit();
|
|
voicePromptsAppendPrompt(PROMPT_SILENCE);
|
|
voicePromptsAppendLanguageString(currentLanguage->mute);
|
|
voicePromptsAppendPrompt(PROMPT_SILENCE);
|
|
voicePromptsAppendLanguageString(currentLanguage->no);
|
|
voicePromptsPlay();
|
|
}
|
|
}
|
|
}
|
|
else // (uiDataGlobal.FreqEnter.index == 0)
|
|
{
|
|
if (KEYCHECK_PRESS(ev->keys, KEY_DECREASE)) // set as KEY_LEFT on some platforms
|
|
{
|
|
uiDataGlobal.FreqEnter.index--;
|
|
uiDataGlobal.FreqEnter.digits[uiDataGlobal.FreqEnter.index] = '-';
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
}
|
|
else if (KEYCHECK_SHORTUP(ev->keys, KEY_RED))
|
|
{
|
|
freqEnterReset();
|
|
soundSetMelody(MELODY_NACK_BEEP);
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_OR_VFO_FREQ, PROMPT_THRESHOLD_NEVER_PLAY_IMMEDIATELY);
|
|
}
|
|
else if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN))
|
|
{
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_NORMAL)
|
|
{
|
|
int newFrequency = freqEnterRead(0, 8, false);
|
|
|
|
if (trxGetBandFromFrequency(newFrequency) != FREQUENCY_OUT_OF_BAND)
|
|
{
|
|
updateFrequency(newFrequency, PROMPT_THRESHOLD_3);
|
|
HRC6000ClearColorCodeSynchronisation();
|
|
freqEnterReset();
|
|
}
|
|
else
|
|
{
|
|
menuVFOExitStatus |= MENU_STATUS_ERROR;
|
|
}
|
|
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
}
|
|
else if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SCAN)
|
|
{
|
|
// Complete frequencies with zeros
|
|
|
|
// Low
|
|
if (uiDataGlobal.FreqEnter.index > 0 && uiDataGlobal.FreqEnter.index < 6)
|
|
{
|
|
memset(uiDataGlobal.FreqEnter.digits + uiDataGlobal.FreqEnter.index, '0', (6 - uiDataGlobal.FreqEnter.index) - 1);
|
|
uiDataGlobal.FreqEnter.index = 5;
|
|
keyval = 0;
|
|
} // High
|
|
else if (uiDataGlobal.FreqEnter.index > 6 && uiDataGlobal.FreqEnter.index < 12)
|
|
{
|
|
memset(uiDataGlobal.FreqEnter.digits + uiDataGlobal.FreqEnter.index, '0', (6 - (uiDataGlobal.FreqEnter.index - 6)) - 1);
|
|
uiDataGlobal.FreqEnter.index = 11;
|
|
keyval = 0;
|
|
}
|
|
else
|
|
{
|
|
if (uiDataGlobal.FreqEnter.index != 0)
|
|
{
|
|
menuVFOExitStatus |= MENU_STATUS_ERROR;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (uiDataGlobal.FreqEnter.index < ((screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_NORMAL) ? 8 : 12))
|
|
{
|
|
// GREEN key was pressed while entering scan freq if keyval is != 99
|
|
if (keyval == 99)
|
|
{
|
|
keyval = menuGetKeypadKeyValue(ev, true);
|
|
|
|
#if !defined(PLATFORM_GD77S)
|
|
if ((keyval < 10) && BUTTONCHECK_DOWN(ev, BUTTON_SK1) && (BUTTONCHECK_DOWN(ev, BUTTON_SK2) == false))
|
|
{
|
|
if (keyval == 1)
|
|
{
|
|
aprsBeaconingToggles();
|
|
}
|
|
else if (keyval == 2)
|
|
{
|
|
switch (aprsBeaconingGetMode())
|
|
{
|
|
case APRS_BEACONING_MODE_MANUAL:
|
|
case APRS_BEACONING_MODE_AUTO:
|
|
case APRS_BEACONING_MODE_SMART_BEACONING:
|
|
if (txInhibitCheckAndWarn() == false)
|
|
{
|
|
aprsBeaconingSendBeacon(false, true);
|
|
}
|
|
break;
|
|
|
|
default: // Ignore other APRS modes.
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if ((keyval != 99) &&
|
|
// Not first '0' digit in frequencies: we don't support < 100 MHz
|
|
((((uiDataGlobal.FreqEnter.index == 0) && (keyval == 0)) == false) &&
|
|
(((screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SCAN) && (uiDataGlobal.FreqEnter.index == 6) && (keyval == 0)) == false)) &&
|
|
(BUTTONCHECK_DOWN(ev, BUTTON_SK2) == 0))
|
|
{
|
|
voicePromptsInit();
|
|
voicePromptsAppendPrompt(PROMPT_0 + keyval);
|
|
if ((uiDataGlobal.FreqEnter.index == 2) || (uiDataGlobal.FreqEnter.index == 8))
|
|
{
|
|
voicePromptsAppendPrompt(PROMPT_POINT);
|
|
}
|
|
voicePromptsPlay();
|
|
|
|
uiDataGlobal.FreqEnter.digits[uiDataGlobal.FreqEnter.index] = (char) keyval + '0';
|
|
uiDataGlobal.FreqEnter.index++;
|
|
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_NORMAL)
|
|
{
|
|
if (uiDataGlobal.FreqEnter.index == 8)
|
|
{
|
|
int newFreq = freqEnterRead(0, 8, false);
|
|
|
|
if (trxGetBandFromFrequency(newFreq) != FREQUENCY_OUT_OF_BAND)
|
|
{
|
|
updateFrequency(newFreq, AUDIO_PROMPT_MODE_BEEP);
|
|
HRC6000ClearColorCodeSynchronisation();
|
|
freqEnterReset();
|
|
soundSetMelody(MELODY_ACK_BEEP);
|
|
}
|
|
else
|
|
{
|
|
uiDataGlobal.FreqEnter.index--;
|
|
uiDataGlobal.FreqEnter.digits[uiDataGlobal.FreqEnter.index] = '-';
|
|
soundSetMelody(MELODY_ERROR_BEEP);
|
|
menuVFOExitStatus |= MENU_STATUS_ERROR;
|
|
}
|
|
}
|
|
}
|
|
else if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SCAN)
|
|
{
|
|
// Check low boundary
|
|
if (uiDataGlobal.FreqEnter.index == 6)
|
|
{
|
|
int fLower = freqEnterRead(0, 6, false) * 100;
|
|
|
|
if (trxGetBandFromFrequency(fLower) == FREQUENCY_OUT_OF_BAND)
|
|
{
|
|
uiDataGlobal.FreqEnter.index--;
|
|
uiDataGlobal.FreqEnter.digits[uiDataGlobal.FreqEnter.index] = '-';
|
|
soundSetMelody(MELODY_ERROR_BEEP);
|
|
menuVFOExitStatus |= MENU_STATUS_ERROR;
|
|
}
|
|
}
|
|
else if (uiDataGlobal.FreqEnter.index == FREQ_ENTER_DIGITS_MAX)
|
|
{
|
|
int fStep = VFO_FREQ_STEP_TABLE[(currentChannelData->VFOflag5 >> 4)];
|
|
int fLower = freqEnterRead(0, 6, false) * 100;
|
|
int fUpper = freqEnterRead(6, 12, false) * 100;
|
|
|
|
// Reorg min/max
|
|
if (fLower > fUpper)
|
|
{
|
|
SAFE_SWAP(fLower, fUpper);
|
|
}
|
|
|
|
// At least on step diff
|
|
if ((fUpper - fLower) < fStep)
|
|
{
|
|
fUpper = fLower + fStep;
|
|
}
|
|
|
|
// Refresh on every step if scan boundaries is equal to one frequency step.
|
|
uiDataGlobal.Scan.refreshOnEveryStep = ((fUpper - fLower) <= fStep);
|
|
|
|
if ((trxGetBandFromFrequency(fLower) != FREQUENCY_OUT_OF_BAND) && (trxGetBandFromFrequency(fUpper) != FREQUENCY_OUT_OF_BAND))
|
|
{
|
|
settingsSet(nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber], (uint32_t) fLower);
|
|
settingsSet(nonVolatileSettings.vfoScanHigh[nonVolatileSettings.currentVFONumber], (uint32_t) fUpper);
|
|
|
|
freqEnterReset();
|
|
soundSetMelody(MELODY_ACK_BEEP);
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_OR_VFO_FREQ, PROMPT_THRESHOLD_3);
|
|
}
|
|
else
|
|
{
|
|
uiDataGlobal.FreqEnter.index--;
|
|
uiDataGlobal.FreqEnter.digits[uiDataGlobal.FreqEnter.index] = '-';
|
|
soundSetMelody(MELODY_ERROR_BEEP);
|
|
menuVFOExitStatus |= MENU_STATUS_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void handleUpKey(uiEvent_t *ev)
|
|
{
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
// Don't permit to switch from RX/TX while scanning
|
|
if ((screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SCAN) &&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_DUAL_SCAN) &&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP))
|
|
{
|
|
selectedFreq = VFO_SELECTED_FREQUENCY_INPUT_RX;
|
|
announceItem(PROMPT_SEQUENCE_DIRECTION_RX, PROMPT_THRESHOLD_1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (uiDataGlobal.Scan.active)
|
|
{
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SWEEP)
|
|
{
|
|
stepFrequency(VFO_SWEEP_SCAN_FREQ_STEP_TABLE[uiDataGlobal.Scan.sweepStepSizeIndex]);
|
|
uiVFOModeUpdateScreen(0);
|
|
vfoSweepUpdateSamples(1, false, 0);
|
|
}
|
|
else
|
|
{
|
|
stepFrequency(VFO_FREQ_STEP_TABLE[(currentChannelData->VFOflag5 >> 4)] * uiDataGlobal.Scan.direction);
|
|
uiDataGlobal.Scan.timer.timeout = 500;
|
|
uiDataGlobal.Scan.state = SCAN_STATE_SCANNING;
|
|
uiVFOModeUpdateScreen(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stepFrequency(VFO_FREQ_STEP_TABLE[(currentChannelData->VFOflag5 >> 4)]);
|
|
uiVFOModeUpdateScreen(0);
|
|
}
|
|
|
|
}
|
|
|
|
settingsSetVFODirty();
|
|
}
|
|
|
|
static void handleDownKey(uiEvent_t *ev)
|
|
{
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
// Don't permit to switch from RX/TX while scanning
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_NORMAL)
|
|
{
|
|
selectedFreq = VFO_SELECTED_FREQUENCY_INPUT_TX;
|
|
announceItem(PROMPT_SEQUENCE_DIRECTION_TX, PROMPT_THRESHOLD_1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SWEEP)
|
|
{
|
|
stepFrequency(VFO_SWEEP_SCAN_FREQ_STEP_TABLE[uiDataGlobal.Scan.sweepStepSizeIndex] * -1);
|
|
}
|
|
else
|
|
{
|
|
stepFrequency(VFO_FREQ_STEP_TABLE[(currentChannelData->VFOflag5 >> 4)] * -1);
|
|
}
|
|
|
|
uiVFOModeUpdateScreen(0);
|
|
settingsSetVFODirty();
|
|
|
|
if (uiDataGlobal.Scan.active && (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SWEEP))
|
|
{
|
|
vfoSweepUpdateSamples(-1, false, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void vfoSweepDrawSample(int offset)
|
|
{
|
|
int16_t graphHeight = MAX(vfoSweepSamples[offset] - vfoSweepRssiNoiseFloor, 0);
|
|
graphHeight = (graphHeight * VFO_SWEEP_GRAPH_HEIGHT_Y) / vfoSweepGain;
|
|
graphHeight = MIN(VFO_SWEEP_GRAPH_HEIGHT_Y, graphHeight);
|
|
|
|
int16_t levelTop = ((VFO_SWEEP_GRAPH_START_Y + VFO_SWEEP_GRAPH_HEIGHT_Y) - graphHeight);
|
|
|
|
// Draw the level
|
|
displayThemeApply(THEME_ITEM_FG_RSSI_BAR, THEME_ITEM_BG);
|
|
displayDrawFastVLine(offset, VFO_SWEEP_GRAPH_START_Y, (VFO_SWEEP_GRAPH_HEIGHT_Y - graphHeight), false); // Clear
|
|
displayDrawFastVLine(offset, levelTop, graphHeight, true); // Level
|
|
|
|
// center freq marker
|
|
if (offset == (DISPLAY_SIZE_X >> 1))
|
|
{
|
|
bool markerTopPosition = (graphHeight < ((VFO_SWEEP_GRAPH_HEIGHT_Y / 3) << 1));
|
|
int16_t markerStarts = (markerTopPosition ? VFO_SWEEP_GRAPH_START_Y : ((VFO_SWEEP_GRAPH_START_Y + VFO_SWEEP_GRAPH_HEIGHT_Y) - (VFO_SWEEP_GRAPH_HEIGHT_Y / 3)));
|
|
int16_t markerEnds = (markerTopPosition ? levelTop : (VFO_SWEEP_GRAPH_START_Y + VFO_SWEEP_GRAPH_HEIGHT_Y));
|
|
|
|
displayThemeApply(THEME_ITEM_FG_DECORATION, THEME_ITEM_BG);
|
|
for (int16_t y = markerStarts; y < markerEnds; y += 2)
|
|
{
|
|
displaySetPixel(offset, y, true);
|
|
displaySetPixel(offset, (y + 1), false);
|
|
}
|
|
}
|
|
|
|
displayThemeResetToDefault();
|
|
}
|
|
|
|
static void vfoSweepUpdateSamples(int offset, bool forceRedraw, int bandwidthRescale)
|
|
{
|
|
const int SHIFT_DISTANCE[7] = {6,6,6,6,8,8,8};
|
|
offset *= SHIFT_DISTANCE[uiDataGlobal.Scan.sweepStepSizeIndex];// real offset in samples;
|
|
|
|
if (offset != 0)
|
|
{
|
|
if (offset > 0)
|
|
{
|
|
uiDataGlobal.Scan.sweepSampleIndex = VFO_SWEEP_NUM_SAMPLES - 1 - offset;
|
|
memcpy(&vfoSweepSamples[0], &vfoSweepSamples[offset], VFO_SWEEP_NUM_SAMPLES - offset);
|
|
memset(&vfoSweepSamples[VFO_SWEEP_NUM_SAMPLES - offset], 0x00, offset);
|
|
}
|
|
else
|
|
{
|
|
uiDataGlobal.Scan.sweepSampleIndex = 0;
|
|
offset *= -1;
|
|
memmove(&vfoSweepSamples[offset], &vfoSweepSamples[0], VFO_SWEEP_NUM_SAMPLES - offset);
|
|
memset(&vfoSweepSamples[0], 0x00, offset);
|
|
}
|
|
}
|
|
|
|
if (bandwidthRescale != 0)
|
|
{
|
|
uint8_t tmp[VFO_SWEEP_NUM_SAMPLES];
|
|
memset(tmp, 0x00, VFO_SWEEP_NUM_SAMPLES);
|
|
int newStartSample = (VFO_SWEEP_NUM_SAMPLES / 2) - (VFO_SWEEP_NUM_SAMPLES / 4);
|
|
|
|
if (bandwidthRescale > 0)
|
|
{
|
|
for(int i = 0; i < VFO_SWEEP_NUM_SAMPLES; i+= 2)
|
|
{
|
|
int average = 0;
|
|
for(int j = 0; j < 2; j++ )
|
|
{
|
|
average += vfoSweepSamples[i + j];
|
|
}
|
|
volatile int outBufPos = (i / 2) + newStartSample;
|
|
tmp[outBufPos] = average / 2;
|
|
}
|
|
uiDataGlobal.Scan.sweepSampleIndex = ((VFO_SWEEP_NUM_SAMPLES * 3) / 4);// Most efficient is to start filling in from the area revealed on the right side of the screen
|
|
}
|
|
else
|
|
{
|
|
int newValue;
|
|
for(int i = 0; i < (VFO_SWEEP_NUM_SAMPLES - 1); i++)
|
|
{
|
|
newValue = (vfoSweepSamples[newStartSample + (i / 2)] + vfoSweepSamples[newStartSample + (i / 2) + 1]) / 2; // use simple 2 point expansion averaging
|
|
tmp[i] = newValue;
|
|
}
|
|
tmp[VFO_SWEEP_NUM_SAMPLES - 1] = newValue;
|
|
|
|
uiDataGlobal.Scan.sweepSampleIndex = 0;
|
|
}
|
|
|
|
memcpy(vfoSweepSamples, tmp, VFO_SWEEP_NUM_SAMPLES * sizeof(uint8_t));
|
|
}
|
|
|
|
if (forceRedraw || (offset != 0))
|
|
{
|
|
for(int i = 0; i < VFO_SWEEP_NUM_SAMPLES; i++)
|
|
{
|
|
vfoSweepDrawSample(i);
|
|
}
|
|
|
|
displayRenderRows(1, ((8 + VFO_SWEEP_GRAPH_HEIGHT_Y) / 8) + 1);
|
|
}
|
|
|
|
if (uiDataGlobal.Scan.state == SCAN_STATE_SCANNING)
|
|
{
|
|
uiDataGlobal.Scan.scanSweepCurrentFreq = currentChannelData->rxFreq + (VFO_SWEEP_SCAN_RANGE_SAMPLE_STEP_TABLE[uiDataGlobal.Scan.sweepStepSizeIndex] * (uiDataGlobal.Scan.sweepSampleIndex - (VFO_SWEEP_NUM_SAMPLES / 2))) / VFO_SWEEP_PIXELS_PER_STEP;
|
|
trxSetFrequency(uiDataGlobal.Scan.scanSweepCurrentFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
ticksTimerStart(&uiDataGlobal.Scan.timer, VFO_SWEEP_STEP_TIME);
|
|
}
|
|
}
|
|
|
|
static void setSweepIncDecSetting(sweepSetting_t type, bool increment)
|
|
{
|
|
bool apply = false;
|
|
uint16_t setting = nonVolatileSettings.vfoSweepSettings;
|
|
int bandwidthRescaleDirection = 0;
|
|
switch (type)
|
|
{
|
|
case SWEEP_SETTING_STEP:
|
|
{
|
|
int oldStepIndex = uiDataGlobal.Scan.sweepStepSizeIndex;
|
|
if (increment)
|
|
{
|
|
uiDataGlobal.Scan.sweepStepSizeIndex = SAFE_MIN(((sizeof(VFO_SWEEP_SCAN_RANGE_SAMPLE_STEP_TABLE) / sizeof(VFO_SWEEP_SCAN_RANGE_SAMPLE_STEP_TABLE[0])) - 1), (uiDataGlobal.Scan.sweepStepSizeIndex + 1));
|
|
bandwidthRescaleDirection = 1;
|
|
}
|
|
else
|
|
{
|
|
uiDataGlobal.Scan.sweepStepSizeIndex = SAFE_MAX(0, (uiDataGlobal.Scan.sweepStepSizeIndex - 1));
|
|
bandwidthRescaleDirection = -1;
|
|
}
|
|
|
|
if (oldStepIndex != uiDataGlobal.Scan.sweepStepSizeIndex)
|
|
{
|
|
apply = true;
|
|
setting = (uiDataGlobal.Scan.sweepStepSizeIndex << 12) | (nonVolatileSettings.vfoSweepSettings & 0xFFF);
|
|
}
|
|
}
|
|
break;
|
|
case SWEEP_SETTING_RSSI:
|
|
{
|
|
if (increment)
|
|
{
|
|
if (vfoSweepRssiNoiseFloor > VFO_SWEEP_RSSI_NOISE_FLOOR_MIN)
|
|
{
|
|
vfoSweepRssiNoiseFloor--;
|
|
apply = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (vfoSweepRssiNoiseFloor < VFO_SWEEP_RSSI_NOISE_FLOOR_MAX)
|
|
{
|
|
vfoSweepRssiNoiseFloor++;
|
|
apply = true;
|
|
}
|
|
}
|
|
|
|
if (apply)
|
|
{
|
|
setting &= 0xF07F;
|
|
setting |= (vfoSweepRssiNoiseFloor << 7);
|
|
}
|
|
}
|
|
break;
|
|
case SWEEP_SETTING_GAIN:
|
|
{
|
|
if (increment)
|
|
{
|
|
if (vfoSweepGain > VFO_SWEEP_GAIN_STEP)
|
|
{
|
|
vfoSweepGain -= VFO_SWEEP_GAIN_STEP;
|
|
apply = true;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
if (vfoSweepGain < VFO_SWEEP_GAIN_MAX)
|
|
{
|
|
vfoSweepGain += VFO_SWEEP_GAIN_STEP;
|
|
apply = true;
|
|
}
|
|
}
|
|
|
|
if (apply)
|
|
{
|
|
setting &= 0xFF80;
|
|
setting |= vfoSweepGain;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
settingsSet(nonVolatileSettings.vfoSweepSettings, setting);
|
|
settingsSaveIfNeeded(true);
|
|
|
|
if (apply)
|
|
{
|
|
vfoSweepUpdateSamples(0, true, bandwidthRescaleDirection);
|
|
}
|
|
}
|
|
|
|
static void stepFrequency(int increment)
|
|
{
|
|
int newTxFreq;
|
|
int newRxFreq;
|
|
|
|
if (selectedFreq == VFO_SELECTED_FREQUENCY_INPUT_TX)
|
|
{
|
|
newTxFreq = currentChannelData->txFreq + increment;
|
|
newRxFreq = currentChannelData->rxFreq; // Needed later for the band limited checking
|
|
}
|
|
else
|
|
{
|
|
// VFO_SELECTED_FREQUENCY_INPUT_RX
|
|
newRxFreq = currentChannelData->rxFreq + increment;
|
|
if (!uiDataGlobal.QuickMenu.tmpTxRxLockMode)
|
|
{
|
|
newTxFreq = currentChannelData->txFreq + increment;
|
|
}
|
|
else
|
|
{
|
|
newTxFreq = currentChannelData->txFreq;// Needed later for the band limited checking
|
|
}
|
|
}
|
|
|
|
// Out of frequency in the current band, update freq to the next or prev band.
|
|
if (trxGetBandFromFrequency(newRxFreq) == FREQUENCY_OUT_OF_BAND)
|
|
{
|
|
int band = trxGetNextOrPrevBandFromFrequency(newRxFreq, (increment > 0));
|
|
|
|
if (band != -1)
|
|
{
|
|
newRxFreq = ((increment > 0) ? RADIO_HARDWARE_FREQUENCY_BANDS[band].minFreq : RADIO_HARDWARE_FREQUENCY_BANDS[band].maxFreq);
|
|
newTxFreq = newRxFreq;
|
|
}
|
|
else
|
|
{
|
|
soundSetMelody(MELODY_ERROR_BEEP);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (trxGetBandFromFrequency(newRxFreq) != FREQUENCY_OUT_OF_BAND)
|
|
{
|
|
currentChannelData->txFreq = newTxFreq;
|
|
currentChannelData->rxFreq = newRxFreq;
|
|
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] != VFO_SCREEN_OPERATION_SWEEP)
|
|
{
|
|
HRC6000ClearColorCodeSynchronisation();
|
|
}
|
|
|
|
if ((uiDataGlobal.Scan.active == false) || (uiDataGlobal.Scan.active && (uiDataGlobal.Scan.state == SCAN_STATE_PAUSED)))
|
|
{
|
|
announceItem(PROMPT_SEQUENCE_VFO_FREQ_UPDATE, PROMPT_THRESHOLD_3);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
soundSetMelody(MELODY_ERROR_BEEP);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------- Quick Menu functions -------------------------------------------------------------------
|
|
menuStatus_t uiVFOModeQuickMenu(uiEvent_t *ev, bool isFirstRun)
|
|
{
|
|
if (isFirstRun)
|
|
{
|
|
if (quickmenuNewChannelHandled)
|
|
{
|
|
quickmenuNewChannelHandled = false;
|
|
menuSystemPopAllAndDisplayRootMenu();
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
|
|
uiDataGlobal.QuickMenu.tmpDmrDestinationFilterLevel = nonVolatileSettings.dmrDestinationFilter;
|
|
uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel = nonVolatileSettings.dmrCcTsFilter;
|
|
uiDataGlobal.QuickMenu.tmpAnalogFilterLevel = nonVolatileSettings.analogFilterLevel;
|
|
uiDataGlobal.QuickMenu.tmpTxRxLockMode = settingsIsOptionBitSet(BIT_TX_RX_FREQ_LOCK);
|
|
uiDataGlobal.QuickMenu.tmpVFONumber = nonVolatileSettings.currentVFONumber;
|
|
uiDataGlobal.QuickMenu.tmpToneScanCSS = toneScanCSS;
|
|
uiDataGlobal.QuickMenu.tmpAudioMute = audioAmpIsMuted();
|
|
|
|
menuDataGlobal.numItems = NUM_VFO_SCREEN_QUICK_MENU_ITEMS;
|
|
|
|
menuDataGlobal.menuOptionsSetQuickkey = 0;
|
|
menuDataGlobal.menuOptionsTimeout = 0;
|
|
menuDataGlobal.newOptionSelected = true;
|
|
|
|
voicePromptsInit();
|
|
voicePromptsAppendPrompt(PROMPT_SILENCE);
|
|
voicePromptsAppendPrompt(PROMPT_SILENCE);
|
|
voicePromptsAppendLanguageString(currentLanguage->quick_menu);
|
|
voicePromptsAppendPrompt(PROMPT_SILENCE);
|
|
voicePromptsAppendPrompt(PROMPT_SILENCE);
|
|
|
|
updateQuickMenuScreen(true);
|
|
return (MENU_STATUS_LIST_TYPE | MENU_STATUS_SUCCESS);
|
|
}
|
|
else
|
|
{
|
|
menuQuickVFOExitStatus = MENU_STATUS_SUCCESS;
|
|
|
|
if (ev->hasEvent || (menuDataGlobal.menuOptionsTimeout > 0))
|
|
{
|
|
handleQuickMenuEvent(ev);
|
|
}
|
|
}
|
|
return menuQuickVFOExitStatus;
|
|
}
|
|
|
|
static bool validateNewChannel(void)
|
|
{
|
|
quickmenuNewChannelHandled = true;
|
|
|
|
if (uiDataGlobal.MessageBox.keyPressed == KEY_GREEN)
|
|
{
|
|
int16_t newChannelIndex;
|
|
|
|
//look for empty channel
|
|
for (newChannelIndex = CODEPLUG_CHANNELS_MIN; newChannelIndex <= CODEPLUG_CHANNELS_MAX; newChannelIndex++)
|
|
{
|
|
if (!codeplugAllChannelsIndexIsInUse(newChannelIndex))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (newChannelIndex <= CODEPLUG_CONTACTS_MAX)
|
|
{
|
|
int currentTS = trxGetDMRTimeSlot();
|
|
char nameBuf[SCREEN_LINE_BUFFER_SIZE];
|
|
CodeplugChannel_t tempChannel = channelScreenChannelData;
|
|
|
|
memcpy(&tempChannel.rxFreq, &settingsVFOChannel[nonVolatileSettings.currentVFONumber].rxFreq, CODEPLUG_CHANNEL_DATA_STRUCT_SIZE - 16);// Don't copy the name of the vfo, which are in the first 16 bytes
|
|
tempChannel.rxTone = currentChannelData->rxTone;
|
|
tempChannel.txTone = currentChannelData->txTone;
|
|
|
|
// Codeplug string aren't NULL terminated.
|
|
snprintf(nameBuf, SCREEN_LINE_BUFFER_SIZE, "%s %d", currentLanguage->new_channel, newChannelIndex);
|
|
memset(&tempChannel.name, 0xFF, sizeof(tempChannel.name));
|
|
memcpy(&tempChannel.name, nameBuf, strlen(nameBuf));
|
|
|
|
// change the TS on the new channel to whatever the radio is currently set to.
|
|
codeplugChannelSetFlag(&tempChannel, CHANNEL_FLAG_TIMESLOT_TWO, ((currentTS != 0) ? 1 : 0));
|
|
|
|
if (codeplugChannelSaveDataForIndex(newChannelIndex, &tempChannel))
|
|
{
|
|
codeplugAllChannelsIndexSetUsed(newChannelIndex); //Set channel index as valid
|
|
}
|
|
|
|
// Check if currentZone is initialized
|
|
if (currentZone.NOT_IN_CODEPLUGDATA_indexNumber == 0xDEADBEEF)
|
|
{
|
|
uiChannelInitializeCurrentZone();
|
|
}
|
|
|
|
// check if its real zone and or the virtual zone "All Channels" whose index is -1
|
|
if (CODEPLUG_ZONE_IS_ALLCHANNELS(currentZone))
|
|
{
|
|
// All Channels virtual zone
|
|
settingsSet(nonVolatileSettings.currentZone, (int16_t) (codeplugZonesGetCount() - 1));//set zone to all channels and channel index to free channel found
|
|
|
|
// Change to the index of the new channel
|
|
codeplugSetLastUsedChannelInZone(currentZone.NOT_IN_CODEPLUGDATA_indexNumber, newChannelIndex);
|
|
|
|
settingsSet(nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_CHANNEL_MODE], nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber]);
|
|
currentZone.NOT_IN_CODEPLUGDATA_numChannelsInZone++;
|
|
}
|
|
else
|
|
{
|
|
if (codeplugZoneAddChannelToZoneAndSave(newChannelIndex, ¤tZone))
|
|
{
|
|
codeplugSetLastUsedChannelInZone(currentZone.NOT_IN_CODEPLUGDATA_indexNumber, (currentZone.NOT_IN_CODEPLUGDATA_numChannelsInZone - 1));
|
|
}
|
|
else
|
|
{
|
|
// channelScreenChannelData wasn't modified, only a new channel has been added, and it's available in AllZone.
|
|
nextKeyBeepMelody = (int16_t *)MELODY_NACK_BEEP;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Channel saving succeeded, now we're sure that channel could
|
|
// be used in the channel screen.
|
|
memcpy(&channelScreenChannelData, &tempChannel, sizeof(tempChannel));
|
|
uiDataGlobal.currentSelectedChannelNumber = newChannelIndex;
|
|
|
|
channelScreenChannelData.rxFreq = 0; // NOT SURE IF THIS IS NECESSARY... Flag to the Channel screen that the channel data is now invalid and needs to be reloaded
|
|
uiDataGlobal.VoicePrompts.inhibitInitial = true;
|
|
tsSetManualOverride(((Channel_t) CHANNEL_CHANNEL), (currentTS + 1)); //copy current TS
|
|
|
|
// Just override TG/PC blindly, if not already set
|
|
if (nonVolatileSettings.overrideTG == 0)
|
|
{
|
|
settingsSet(nonVolatileSettings.overrideTG, trxTalkGroupOrPcId);
|
|
}
|
|
|
|
menuSystemPopAllAndDisplaySpecificRootMenu(UI_CHANNEL_MODE, true);
|
|
nextKeyBeepMelody = (int16_t *)MELODY_ACK_BEEP;
|
|
quickmenuNewChannelHandled = false; // Need to do this, as uiVFOModeQuickMenu() won't be re-entered on the next menu iteration
|
|
return true;
|
|
}
|
|
|
|
nextKeyBeepMelody = (int16_t *)MELODY_NACK_BEEP;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void updateQuickMenuScreen(bool isFirstRun)
|
|
{
|
|
int mNum = 0;
|
|
char buf[SCREEN_LINE_BUFFER_SIZE];
|
|
const char *leftSide;// initialise to please the compiler
|
|
const char *rightSideConst;// initialise to please the compiler
|
|
char rightSideVar[SCREEN_LINE_BUFFER_SIZE];
|
|
int prompt;// For voice prompts
|
|
|
|
displayClearBuf();
|
|
bool settingOption = uiQuickKeysShowChoices(buf, SCREEN_LINE_BUFFER_SIZE, currentLanguage->quick_menu);
|
|
|
|
for (int i = MENU_START_ITERATION_VALUE; i <= MENU_END_ITERATION_VALUE; i++)
|
|
{
|
|
if ((settingOption == false) || (i == 0))
|
|
{
|
|
mNum = menuGetMenuOffset(NUM_VFO_SCREEN_QUICK_MENU_ITEMS, i);
|
|
if (mNum == MENU_OFFSET_BEFORE_FIRST_ENTRY)
|
|
{
|
|
continue;
|
|
}
|
|
else if (mNum == MENU_OFFSET_AFTER_LAST_ENTRY)
|
|
{
|
|
break;
|
|
}
|
|
|
|
prompt = -1;// Prompt not used
|
|
buf[0] = 0;
|
|
rightSideVar[0] = 0;
|
|
rightSideConst = NULL;
|
|
leftSide = NULL;
|
|
|
|
switch(mNum)
|
|
{
|
|
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_RD5R) || defined(PLATFORM_DM1801A) || defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
case VFO_SCREEN_QUICK_MENU_VFO_A_B:
|
|
sprintf(rightSideVar, "VFO %c", ((uiDataGlobal.QuickMenu.tmpVFONumber == 0) ? 'A' : 'B'));
|
|
break;
|
|
#endif
|
|
case VFO_SCREEN_QUICK_MENU_TX_SWAP_RX:
|
|
prompt = PROMPT_VFO_EXCHANGE_TX_RX;
|
|
strcpy(rightSideVar, "Tx <--> Rx");
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_BOTH_TO_RX:
|
|
prompt = PROMPT_VFO_COPY_RX_TO_TX;
|
|
strcpy(rightSideVar, "Rx --> Tx");
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_BOTH_TO_TX:
|
|
prompt = PROMPT_VFO_COPY_TX_TO_RX;
|
|
strcpy(rightSideVar, "Tx --> Rx");
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_FILTER_FM:
|
|
leftSide = currentLanguage->filter;
|
|
if (uiDataGlobal.QuickMenu.tmpAnalogFilterLevel == 0)
|
|
{
|
|
rightSideConst = currentLanguage->none;
|
|
}
|
|
else
|
|
{
|
|
snprintf(rightSideVar, SCREEN_LINE_BUFFER_SIZE, "%s", ANALOG_FILTER_LEVELS[uiDataGlobal.QuickMenu.tmpAnalogFilterLevel - 1]);
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_FILTER_DMR:
|
|
leftSide = currentLanguage->dmr_filter;
|
|
if (uiDataGlobal.QuickMenu.tmpDmrDestinationFilterLevel == 0)
|
|
{
|
|
rightSideConst = currentLanguage->none;
|
|
}
|
|
else
|
|
{
|
|
snprintf(rightSideVar, SCREEN_LINE_BUFFER_SIZE, "%s", DMR_DESTINATION_FILTER_LEVELS[uiDataGlobal.QuickMenu.tmpDmrDestinationFilterLevel - 1]);
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_DMR_CC_SCAN:
|
|
leftSide = currentLanguage->dmr_cc_scan;
|
|
rightSideConst = (uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel & DMR_CC_FILTER_PATTERN) ? currentLanguage->off : currentLanguage->on;
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_FILTER_DMR_TS:
|
|
leftSide = currentLanguage->dmr_ts_filter;
|
|
rightSideConst = (uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel & DMR_TS_FILTER_PATTERN) ? currentLanguage->on : currentLanguage->off;
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_VFO_TO_NEW:
|
|
rightSideConst = currentLanguage->vfoToNewChannel;
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_TONE_SCAN:
|
|
leftSide = currentLanguage->tone_scan;
|
|
if(trxGetMode() == RADIO_MODE_ANALOG)
|
|
{
|
|
const char *scanCSS[] = { currentLanguage->all, "CTCSS", "DCS", "iDCS" };
|
|
uint8_t offset = 0;
|
|
|
|
if (uiDataGlobal.QuickMenu.tmpToneScanCSS == CSS_TYPE_NONE)
|
|
{
|
|
offset = 0;
|
|
}
|
|
else if (uiDataGlobal.QuickMenu.tmpToneScanCSS == CSS_TYPE_CTCSS)
|
|
{
|
|
offset = 1;
|
|
}
|
|
else if (uiDataGlobal.QuickMenu.tmpToneScanCSS == CSS_TYPE_DCS)
|
|
{
|
|
offset = 2;
|
|
}
|
|
else if (uiDataGlobal.QuickMenu.tmpToneScanCSS == (CSS_TYPE_DCS | CSS_TYPE_DCS_INVERTED))
|
|
{
|
|
offset = 3;
|
|
}
|
|
|
|
snprintf(rightSideVar, SCREEN_LINE_BUFFER_SIZE, "%s", scanCSS[offset]);
|
|
|
|
if (uiDataGlobal.QuickMenu.tmpToneScanCSS == CSS_TYPE_NONE)
|
|
{
|
|
rightSideConst = currentLanguage->all;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rightSideConst = currentLanguage->n_a;
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_DUAL_SCAN:
|
|
rightSideConst = currentLanguage->dual_watch;
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_FREQ_BIND_MODE:
|
|
leftSide = currentLanguage->vfo_freq_bind_mode;
|
|
rightSideConst = (!uiDataGlobal.QuickMenu.tmpTxRxLockMode) ? currentLanguage->on : currentLanguage->off;
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_AUDIO_MUTE:
|
|
leftSide = currentLanguage->mute;
|
|
rightSideConst = (uiDataGlobal.QuickMenu.tmpAudioMute ? currentLanguage->yes : currentLanguage->no);
|
|
break;
|
|
default:
|
|
strcpy(buf, "");
|
|
break;
|
|
}
|
|
|
|
if (leftSide != NULL)
|
|
{
|
|
snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "%s:%s", leftSide, (rightSideVar[0] ? rightSideVar : rightSideConst));
|
|
}
|
|
else
|
|
{
|
|
snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "%s", ((rightSideVar[0] != 0) ? rightSideVar : rightSideConst));
|
|
}
|
|
|
|
if (i == 0)
|
|
{
|
|
if (!isFirstRun && (menuDataGlobal.menuOptionsSetQuickkey == 0))
|
|
{
|
|
voicePromptsInit();
|
|
}
|
|
|
|
if (prompt != -1)
|
|
{
|
|
voicePromptsAppendPrompt(prompt);
|
|
}
|
|
else
|
|
{
|
|
if ((leftSide != NULL) || menuDataGlobal.newOptionSelected)
|
|
{
|
|
voicePromptsAppendLanguageString(leftSide);
|
|
}
|
|
|
|
if ((rightSideVar[0] != 0) && (rightSideConst == NULL))
|
|
{
|
|
voicePromptsAppendString(rightSideVar);
|
|
}
|
|
else
|
|
{
|
|
voicePromptsAppendLanguageString(rightSideConst);
|
|
}
|
|
}
|
|
|
|
if (menuDataGlobal.menuOptionsTimeout != -1)
|
|
{
|
|
promptsPlayNotAfterTx();
|
|
}
|
|
else
|
|
{
|
|
menuDataGlobal.menuOptionsTimeout = 0;// clear flag indicating that a QuickKey has just been set
|
|
}
|
|
}
|
|
|
|
// QuickKeys
|
|
if (menuDataGlobal.menuOptionsTimeout > 0)
|
|
{
|
|
menuDisplaySettingOption(leftSide, (rightSideVar[0] ? rightSideVar : rightSideConst));
|
|
}
|
|
else
|
|
{
|
|
switch (mNum)
|
|
{
|
|
case VFO_SCREEN_QUICK_MENU_FILTER_FM:
|
|
case VFO_SCREEN_QUICK_MENU_FILTER_DMR:
|
|
case VFO_SCREEN_QUICK_MENU_DMR_CC_SCAN:
|
|
case VFO_SCREEN_QUICK_MENU_FILTER_DMR_TS:
|
|
case VFO_SCREEN_QUICK_MENU_TONE_SCAN:
|
|
case VFO_SCREEN_QUICK_MENU_FREQ_BIND_MODE:
|
|
case VFO_SCREEN_QUICK_MENU_AUDIO_MUTE:
|
|
menuDisplayEntry(i, mNum, buf, (strlen(leftSide) + 1), THEME_ITEM_FG_MENU_ITEM, THEME_ITEM_FG_OPTIONS_VALUE, THEME_ITEM_BG);
|
|
break;
|
|
|
|
default:
|
|
menuDisplayEntry(i, mNum, buf, 0, THEME_ITEM_FG_MENU_ITEM, THEME_ITEM_COLOUR_NONE, THEME_ITEM_BG);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
displayRender();
|
|
}
|
|
|
|
static void handleQuickMenuEvent(uiEvent_t *ev)
|
|
{
|
|
bool isDirty = false;
|
|
bool executingQuickKey = false;
|
|
|
|
if ((menuDataGlobal.menuOptionsTimeout > 0) && (!BUTTONCHECK_DOWN(ev, BUTTON_SK2)))
|
|
{
|
|
menuDataGlobal.menuOptionsTimeout--;
|
|
if (menuDataGlobal.menuOptionsTimeout == 0)
|
|
{
|
|
// Let the QuickKey's VP playback to ends before
|
|
// going back to the previous menu
|
|
if (voicePromptsIsPlaying())
|
|
{
|
|
menuDataGlobal.menuOptionsTimeout++;
|
|
return;
|
|
}
|
|
|
|
menuSystemPopPreviousMenu();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ev->events & BUTTON_EVENT)
|
|
{
|
|
if (repeatVoicePromptOnSK1(ev))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ev->events & FUNCTION_EVENT)
|
|
{
|
|
isDirty = true;
|
|
if (ev->function == FUNC_REDRAW)
|
|
{
|
|
updateQuickMenuScreen(false);
|
|
return;
|
|
}
|
|
else if ((QUICKKEY_TYPE(ev->function) == QUICKKEY_MENU) && (QUICKKEY_ENTRYID(ev->function) < NUM_VFO_SCREEN_QUICK_MENU_ITEMS))
|
|
{
|
|
menuDataGlobal.currentItemIndex = QUICKKEY_ENTRYID(ev->function);
|
|
}
|
|
|
|
if ((QUICKKEY_FUNCTIONID(ev->function) != 0))
|
|
{
|
|
menuDataGlobal.menuOptionsTimeout = 1000;
|
|
executingQuickKey = true;
|
|
}
|
|
}
|
|
|
|
if ((ev->events & (KEY_EVENT | BUTTON_EVENT)) && (menuDataGlobal.menuOptionsSetQuickkey == 0) && (menuDataGlobal.menuOptionsTimeout == 0))
|
|
{
|
|
if (KEYCHECK_PRESS(ev->keys, KEY_DOWN) && (menuDataGlobal.numItems != 0))
|
|
{
|
|
isDirty = true;
|
|
menuSystemMenuIncrement(&menuDataGlobal.currentItemIndex, NUM_VFO_SCREEN_QUICK_MENU_ITEMS);
|
|
menuDataGlobal.newOptionSelected = true;
|
|
menuQuickVFOExitStatus |= MENU_STATUS_LIST_TYPE;
|
|
}
|
|
else if (KEYCHECK_PRESS(ev->keys, KEY_UP))
|
|
{
|
|
isDirty = true;
|
|
menuSystemMenuDecrement(&menuDataGlobal.currentItemIndex, NUM_VFO_SCREEN_QUICK_MENU_ITEMS);
|
|
menuDataGlobal.newOptionSelected = true;
|
|
menuQuickVFOExitStatus |= MENU_STATUS_LIST_TYPE;
|
|
}
|
|
else if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN))
|
|
{
|
|
|
|
quickKeyApply: // branching here when to quickkey was used.
|
|
|
|
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_RD5R) || defined(PLATFORM_DM1801A) || defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
if (nonVolatileSettings.currentVFONumber != uiDataGlobal.QuickMenu.tmpVFONumber)
|
|
{
|
|
settingsSet(nonVolatileSettings.currentVFONumber, uiDataGlobal.QuickMenu.tmpVFONumber);
|
|
currentChannelData = &settingsVFOChannel[nonVolatileSettings.currentVFONumber];
|
|
}
|
|
#endif
|
|
toneScanCSS = uiDataGlobal.QuickMenu.tmpToneScanCSS;
|
|
|
|
switch(menuDataGlobal.currentItemIndex)
|
|
{
|
|
case VFO_SCREEN_QUICK_MENU_TX_SWAP_RX:
|
|
{
|
|
int tmpFreq = currentChannelData->txFreq;
|
|
currentChannelData->txFreq = currentChannelData->rxFreq;
|
|
currentChannelData->rxFreq = tmpFreq;
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_OR_VFO_FREQ, PROMPT_THRESHOLD_3);
|
|
}
|
|
break;
|
|
|
|
case VFO_SCREEN_QUICK_MENU_BOTH_TO_RX:
|
|
currentChannelData->txFreq = currentChannelData->rxFreq;
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_OR_VFO_FREQ, PROMPT_THRESHOLD_3);
|
|
break;
|
|
|
|
case VFO_SCREEN_QUICK_MENU_BOTH_TO_TX:
|
|
currentChannelData->rxFreq = currentChannelData->txFreq;
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_OR_VFO_FREQ, PROMPT_THRESHOLD_3);
|
|
break;
|
|
|
|
case VFO_SCREEN_QUICK_MENU_VFO_TO_NEW:
|
|
if (quickmenuNewChannelHandled == false)
|
|
{
|
|
snprintf(uiDataGlobal.MessageBox.message, MESSAGEBOX_MESSAGE_LEN_MAX, "%s\n%s", currentLanguage->new_channel, currentLanguage->please_confirm);
|
|
uiDataGlobal.MessageBox.type = MESSAGEBOX_TYPE_INFO;
|
|
uiDataGlobal.MessageBox.decoration = MESSAGEBOX_DECORATION_FRAME;
|
|
uiDataGlobal.MessageBox.buttons = MESSAGEBOX_BUTTONS_YESNO;
|
|
uiDataGlobal.MessageBox.validatorCallback = validateNewChannel;
|
|
menuSystemPushNewMenu(UI_MESSAGE_BOX);
|
|
|
|
voicePromptsInit();
|
|
voicePromptsAppendLanguageString(currentLanguage->new_channel);
|
|
voicePromptsAppendLanguageString(currentLanguage->please_confirm);
|
|
voicePromptsPlay();
|
|
}
|
|
return;
|
|
break;
|
|
|
|
case VFO_SCREEN_QUICK_MENU_TONE_SCAN:
|
|
if (trxGetMode() == RADIO_MODE_ANALOG)
|
|
{
|
|
trxSetAnalogFilterLevel(ANALOG_FILTER_CSS);
|
|
bool cssTypesDiffer = false;
|
|
CodeplugCSSTypes_t currentCSSType = codeplugGetCSSType(currentChannelData->rxTone);
|
|
|
|
// Check if the current CSS differs from the one set to scan.
|
|
if (((currentCSSType & CSS_TYPE_NONE) == 0) && ((toneScanCSS & CSS_TYPE_NONE) == 0) && (toneScanCSS != currentCSSType))
|
|
{
|
|
cssTypesDiffer = true;
|
|
}
|
|
|
|
// CTCSS or DCS no CSS
|
|
toneScanType = (((toneScanCSS & CSS_TYPE_NONE) == 0) ? toneScanCSS : currentCSSType);
|
|
prevCSSTone = currentChannelData->rxTone;
|
|
|
|
if ((currentCSSType == CSS_TYPE_NONE) || cssTypesDiffer)
|
|
{
|
|
// CSS type are different, start from index 0
|
|
scanToneIndex = 0;
|
|
if (toneScanType == CSS_TYPE_NONE)
|
|
{
|
|
toneScanType = CSS_TYPE_CTCSS;
|
|
}
|
|
currentChannelData->rxTone = cssGetToneFromIndex(scanToneIndex, toneScanType);
|
|
}
|
|
else
|
|
{
|
|
// Get the tone index in the current type array.
|
|
scanToneIndex = cssGetToneIndex(currentChannelData->rxTone, toneScanType);
|
|
|
|
// Set the tone to the next one
|
|
cssIncrement(¤tChannelData->rxTone, &scanToneIndex, 1, &toneScanType, true, (toneScanCSS != CSS_TYPE_NONE));
|
|
}
|
|
|
|
audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
|
|
trxRxAndTxOff(true);
|
|
trxSetRxCSS(RADIO_DEVICE_PRIMARY, currentChannelData->rxTone);
|
|
trxRxOn(true);
|
|
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiDataGlobal.Scan.toneActive = true;
|
|
uiDataGlobal.Scan.refreshOnEveryStep = false;
|
|
uiDataGlobal.Scan.timer.timeout = ((toneScanType == CSS_TYPE_CTCSS) ? (SCAN_TONE_INTERVAL - (scanToneIndex * 2)) : SCAN_TONE_INTERVAL);
|
|
uiDataGlobal.Scan.direction = 1;
|
|
}
|
|
break;
|
|
|
|
case VFO_SCREEN_QUICK_MENU_DUAL_SCAN:
|
|
uiDataGlobal.Scan.active = true;
|
|
uiDataGlobal.Scan.stepTimeMilliseconds = settingsGetScanStepTimeMilliseconds();
|
|
uiDataGlobal.Scan.dwellTime = 135;// for Dual Watch, use a larger step time than normally scanning, and which does not synchronise with the DMR 30ms timeslots
|
|
uiDataGlobal.Scan.timer.timeout = uiDataGlobal.Scan.dwellTime;
|
|
uiDataGlobal.Scan.refreshOnEveryStep = false;
|
|
screenOperationMode[CHANNEL_VFO_A] = screenOperationMode[CHANNEL_VFO_B] = VFO_SCREEN_OPERATION_DUAL_SCAN;
|
|
uiDataGlobal.VoicePrompts.inhibitInitial = true;
|
|
uiDataGlobal.Scan.scanType = SCAN_TYPE_DUAL_WATCH;
|
|
int currentPowerSavingLevel = rxPowerSavingGetLevel();
|
|
if (currentPowerSavingLevel > 1)
|
|
{
|
|
rxPowerSavingSetLevel(currentPowerSavingLevel - 1);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// VFO_SCREEN_QUICK_MENU_FILTER_FM
|
|
if (nonVolatileSettings.analogFilterLevel != uiDataGlobal.QuickMenu.tmpAnalogFilterLevel)
|
|
{
|
|
settingsSet(nonVolatileSettings.analogFilterLevel, uiDataGlobal.QuickMenu.tmpAnalogFilterLevel);
|
|
trxSetAnalogFilterLevel(nonVolatileSettings.analogFilterLevel);
|
|
}
|
|
|
|
// VFO_SCREEN_QUICK_MENU_FILTER_DMR
|
|
if (nonVolatileSettings.dmrDestinationFilter != uiDataGlobal.QuickMenu.tmpDmrDestinationFilterLevel)
|
|
{
|
|
settingsSet(nonVolatileSettings.dmrDestinationFilter, uiDataGlobal.QuickMenu.tmpDmrDestinationFilterLevel);
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
HRC6000InitDigitalDmrRx();
|
|
audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
|
|
}
|
|
}
|
|
|
|
// VFO_SCREEN_QUICK_MENU_DMR_CC_FILTER
|
|
// VFO_SCREEN_QUICK_MENU_FILTER_DMR_TS
|
|
if (nonVolatileSettings.dmrCcTsFilter != uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel)
|
|
{
|
|
settingsSet(nonVolatileSettings.dmrCcTsFilter, uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel);
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
HRC6000InitDigitalDmrRx();
|
|
HRC6000ResyncTimeSlot();
|
|
audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
|
|
}
|
|
}
|
|
|
|
// VFO_SCREEN_QUICK_MENU_FREQ_BIND_MODE
|
|
if (settingsIsOptionBitSet(BIT_TX_RX_FREQ_LOCK) != uiDataGlobal.QuickMenu.tmpTxRxLockMode)
|
|
{
|
|
settingsSetOptionBit(BIT_TX_RX_FREQ_LOCK, uiDataGlobal.QuickMenu.tmpTxRxLockMode);
|
|
}
|
|
|
|
// VFO_SCREEN_QUICK_MENU_MUTE
|
|
if (audioAmpIsMuted() != uiDataGlobal.QuickMenu.tmpAudioMute)
|
|
{
|
|
audioAmpMute(uiDataGlobal.QuickMenu.tmpAudioMute);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (executingQuickKey)
|
|
{
|
|
updateQuickMenuScreen(false);
|
|
}
|
|
else
|
|
{
|
|
menuSystemPopPreviousMenu();
|
|
}
|
|
return;
|
|
}
|
|
else if (KEYCHECK_SHORTUP(ev->keys, KEY_RED))
|
|
{
|
|
uiVFOModeStopScanning();
|
|
menuSystemPopPreviousMenu();
|
|
return;
|
|
}
|
|
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_RD5R) || defined(PLATFORM_DM1801A) || defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_DM1801A) || defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
else if (((ev->events & BUTTON_EVENT) && BUTTONCHECK_SHORTUP(ev, BUTTON_ORANGE)) && (menuDataGlobal.currentItemIndex == VFO_SCREEN_QUICK_MENU_VFO_A_B))
|
|
#elif defined(PLATFORM_RD5R)
|
|
else if (KEYCHECK_SHORTUP(ev->keys, KEY_VFO_MR) && (menuDataGlobal.currentItemIndex == VFO_SCREEN_QUICK_MENU_VFO_A_B))
|
|
#endif
|
|
{
|
|
settingsSet(nonVolatileSettings.currentVFONumber, (1 - uiDataGlobal.QuickMenu.tmpVFONumber));// Switch to other VFO
|
|
currentChannelData = &settingsVFOChannel[nonVolatileSettings.currentVFONumber];
|
|
menuSystemPopPreviousMenu();
|
|
if (nonVolatileSettings.currentVFONumber == 0)
|
|
{
|
|
// Trick to beep (AudioAssist), since ORANGE button doesn't produce any beep event
|
|
ev->keys.event |= KEY_MOD_UP;
|
|
ev->keys.key = 127;
|
|
// End Trick
|
|
|
|
menuQuickVFOExitStatus |= MENU_STATUS_FORCE_FIRST;
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
else if (KEYCHECK_SHORTUP_NUMBER(ev->keys) && BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
isDirty = true;
|
|
menuDataGlobal.menuOptionsSetQuickkey = ev->keys.key;
|
|
}
|
|
}
|
|
|
|
|
|
if ((ev->events & (KEY_EVENT | FUNCTION_EVENT)) && (menuDataGlobal.menuOptionsSetQuickkey == 0))
|
|
{
|
|
if (KEYCHECK_PRESS(ev->keys, KEY_RIGHT)
|
|
#if defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
|| KEYCHECK_SHORTUP(ev->keys, KEY_ROTARY_INCREMENT)
|
|
#endif
|
|
|| (QUICKKEY_FUNCTIONID(ev->function) == FUNC_RIGHT))
|
|
{
|
|
if (menuDataGlobal.menuOptionsTimeout > 0)
|
|
{
|
|
menuDataGlobal.menuOptionsTimeout = 1000;
|
|
}
|
|
isDirty = true;
|
|
menuDataGlobal.newOptionSelected = false;
|
|
|
|
switch(menuDataGlobal.currentItemIndex)
|
|
{
|
|
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_RD5R) || defined(PLATFORM_DM1801A) || defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
case VFO_SCREEN_QUICK_MENU_VFO_A_B:
|
|
if (uiDataGlobal.QuickMenu.tmpVFONumber == 0)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpVFONumber = 1;
|
|
}
|
|
break;
|
|
#endif
|
|
case VFO_SCREEN_QUICK_MENU_FILTER_FM:
|
|
if (uiDataGlobal.QuickMenu.tmpAnalogFilterLevel < NUM_ANALOG_FILTER_LEVELS - 1)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpAnalogFilterLevel++;
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_FILTER_DMR:
|
|
if (uiDataGlobal.QuickMenu.tmpDmrDestinationFilterLevel < NUM_DMR_DESTINATION_FILTER_LEVELS - 1)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpDmrDestinationFilterLevel++;
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_DMR_CC_SCAN:
|
|
if (uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel & DMR_CC_FILTER_PATTERN)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel &= ~DMR_CC_FILTER_PATTERN;
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_FILTER_DMR_TS:
|
|
if (!(uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel & DMR_TS_FILTER_PATTERN))
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel |= DMR_TS_FILTER_PATTERN;
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_TONE_SCAN:
|
|
if (trxGetMode() == RADIO_MODE_ANALOG)
|
|
{
|
|
if (uiDataGlobal.QuickMenu.tmpToneScanCSS == CSS_TYPE_NONE)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpToneScanCSS = CSS_TYPE_CTCSS;
|
|
}
|
|
else if (uiDataGlobal.QuickMenu.tmpToneScanCSS == CSS_TYPE_CTCSS)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpToneScanCSS = CSS_TYPE_DCS;
|
|
}
|
|
else if (uiDataGlobal.QuickMenu.tmpToneScanCSS == CSS_TYPE_DCS)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpToneScanCSS = (CSS_TYPE_DCS | CSS_TYPE_DCS_INVERTED);
|
|
}
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_FREQ_BIND_MODE:
|
|
uiDataGlobal.QuickMenu.tmpTxRxLockMode = false;
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_AUDIO_MUTE:
|
|
uiDataGlobal.QuickMenu.tmpAudioMute = false;
|
|
break;
|
|
}
|
|
|
|
if (executingQuickKey) // Instantly apply new setting
|
|
{
|
|
goto quickKeyApply;
|
|
}
|
|
}
|
|
else if (KEYCHECK_PRESS(ev->keys, KEY_LEFT)
|
|
#if defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
|| KEYCHECK_SHORTUP(ev->keys, KEY_ROTARY_DECREMENT)
|
|
#endif
|
|
|| (QUICKKEY_FUNCTIONID(ev->function) == FUNC_LEFT))
|
|
{
|
|
if (menuDataGlobal.menuOptionsTimeout > 0)
|
|
{
|
|
menuDataGlobal.menuOptionsTimeout = 1000;
|
|
}
|
|
isDirty = true;
|
|
menuDataGlobal.newOptionSelected = false;
|
|
|
|
switch(menuDataGlobal.currentItemIndex)
|
|
{
|
|
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_RD5R) || defined(PLATFORM_DM1801A) || defined(PLATFORM_MD9600) || defined(PLATFORM_MDUV380) || defined(PLATFORM_MD380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
case VFO_SCREEN_QUICK_MENU_VFO_A_B:
|
|
if (uiDataGlobal.QuickMenu.tmpVFONumber == 1)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpVFONumber = 0;
|
|
}
|
|
menuQuickVFOExitStatus |= MENU_STATUS_FORCE_FIRST;
|
|
break;
|
|
#endif
|
|
case VFO_SCREEN_QUICK_MENU_FILTER_FM:
|
|
if (uiDataGlobal.QuickMenu.tmpAnalogFilterLevel > ANALOG_FILTER_NONE)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpAnalogFilterLevel--;
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_FILTER_DMR:
|
|
if (uiDataGlobal.QuickMenu.tmpDmrDestinationFilterLevel > DMR_DESTINATION_FILTER_NONE)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpDmrDestinationFilterLevel--;
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_DMR_CC_SCAN:
|
|
if (!(uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel & DMR_CC_FILTER_PATTERN))
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel |= DMR_CC_FILTER_PATTERN;
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_FILTER_DMR_TS:
|
|
if (uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel & DMR_TS_FILTER_PATTERN)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpDmrCcTsFilterLevel &= ~DMR_TS_FILTER_PATTERN;
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_TONE_SCAN:
|
|
if (trxGetMode() == RADIO_MODE_ANALOG)
|
|
{
|
|
if (uiDataGlobal.QuickMenu.tmpToneScanCSS == CSS_TYPE_CTCSS)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpToneScanCSS = CSS_TYPE_NONE;
|
|
}
|
|
else if (uiDataGlobal.QuickMenu.tmpToneScanCSS == CSS_TYPE_DCS)
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpToneScanCSS = CSS_TYPE_CTCSS;
|
|
}
|
|
else if (uiDataGlobal.QuickMenu.tmpToneScanCSS == (CSS_TYPE_DCS | CSS_TYPE_DCS_INVERTED))
|
|
{
|
|
uiDataGlobal.QuickMenu.tmpToneScanCSS = CSS_TYPE_DCS;
|
|
}
|
|
}
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_FREQ_BIND_MODE:
|
|
uiDataGlobal.QuickMenu.tmpTxRxLockMode = true;
|
|
break;
|
|
case VFO_SCREEN_QUICK_MENU_AUDIO_MUTE:
|
|
uiDataGlobal.QuickMenu.tmpAudioMute = true;
|
|
break;
|
|
}
|
|
|
|
if (executingQuickKey) // Instantly apply new setting
|
|
{
|
|
goto quickKeyApply;
|
|
}
|
|
}
|
|
else if ((ev->keys.event & KEY_MOD_PRESS) && (menuDataGlobal.menuOptionsTimeout > 0))
|
|
{
|
|
menuDataGlobal.menuOptionsTimeout = 0;
|
|
menuSystemPopPreviousMenu();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (uiQuickKeysIsStoring(ev))
|
|
{
|
|
uiQuickKeysStore(ev, &menuQuickVFOExitStatus);
|
|
isDirty = true;
|
|
}
|
|
|
|
if (isDirty)
|
|
{
|
|
updateQuickMenuScreen(false);
|
|
}
|
|
}
|
|
|
|
bool uiVFOModeIsScanning(void)
|
|
{
|
|
return (uiDataGlobal.Scan.toneActive || uiDataGlobal.Scan.active);
|
|
}
|
|
|
|
bool uiVFOModeDualWatchIsScanning(void)
|
|
{
|
|
return ((menuSystemGetCurrentMenuNumber() == UI_VFO_MODE) && uiDataGlobal.Scan.active &&
|
|
(uiDataGlobal.Scan.state == SCAN_STATE_SCANNING) && (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_DUAL_SCAN));
|
|
}
|
|
|
|
bool uiVFOModeSweepScanning(bool includePaused)
|
|
{
|
|
return ((menuSystemGetCurrentMenuNumber() == UI_VFO_MODE) &&
|
|
uiDataGlobal.Scan.active &&
|
|
(includePaused ? ((uiDataGlobal.Scan.state == SCAN_STATE_SCANNING) || (uiDataGlobal.Scan.state == SCAN_STATE_PAUSED)) : (uiDataGlobal.Scan.state == SCAN_STATE_SCANNING)) &&
|
|
(screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SWEEP));
|
|
}
|
|
|
|
bool uiVFOModeFrequencyScanningIsActiveAndEnabled(uint32_t *lowFreq, uint32_t *highFreq)
|
|
{
|
|
bool ret = ((menuSystemGetCurrentMenuNumber() == UI_VFO_MODE) && (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SCAN));
|
|
|
|
if (ret && lowFreq && highFreq)
|
|
{
|
|
*lowFreq = nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber];
|
|
*highFreq = nonVolatileSettings.vfoScanHigh[nonVolatileSettings.currentVFONumber];
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void toneScan(void)
|
|
{
|
|
if (audioAmpGetStatus() & AUDIO_AMP_CHANNEL_RF)
|
|
{
|
|
currentChannelData->txTone = currentChannelData->rxTone;
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiVFOModeUpdateScreen(0);
|
|
prevCSSTone = (CODEPLUG_CSS_TONE_NONE - 1);
|
|
uiDataGlobal.Scan.toneActive = false;
|
|
return;
|
|
}
|
|
|
|
if (uiDataGlobal.Scan.timer.timeout > 0)
|
|
{
|
|
uiDataGlobal.Scan.timer.timeout--;
|
|
}
|
|
else
|
|
{
|
|
if (uiDataGlobal.Scan.direction == 1)
|
|
{
|
|
cssIncrement(¤tChannelData->rxTone, &scanToneIndex, 1, &toneScanType, true, (toneScanCSS != CSS_TYPE_NONE));
|
|
}
|
|
else
|
|
{
|
|
cssDecrement(¤tChannelData->rxTone, &scanToneIndex, 1, &toneScanType, true, (toneScanCSS != CSS_TYPE_NONE));
|
|
}
|
|
trxRxAndTxOff(true);
|
|
trxSetRxCSS(RADIO_DEVICE_PRIMARY, currentChannelData->rxTone);
|
|
uiDataGlobal.Scan.timer.timeout = ((toneScanType == CSS_TYPE_CTCSS) ? (SCAN_TONE_INTERVAL - (scanToneIndex * 2)) : SCAN_TONE_INTERVAL);
|
|
trxRxOn(true);
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
uiVFOModeUpdateScreen(0);
|
|
}
|
|
}
|
|
|
|
static void updateTrxID(void)
|
|
{
|
|
if (nonVolatileSettings.overrideTG != 0)
|
|
{
|
|
trxTalkGroupOrPcId = nonVolatileSettings.overrideTG;
|
|
}
|
|
else
|
|
{
|
|
//tsSetManualOverride(((Channel_t)nonVolatileSettings.currentVFONumber), TS_NO_OVERRIDE);
|
|
|
|
// Check if this channel has an Rx Group
|
|
if ((currentRxGroupData.name[0] != 0) && (nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber] < currentRxGroupData.NOT_IN_CODEPLUG_numTGsInGroup))
|
|
{
|
|
codeplugContactGetDataForIndex(currentRxGroupData.contacts[nonVolatileSettings.currentIndexInTRxGroupList[SETTINGS_VFO_A_MODE + nonVolatileSettings.currentVFONumber]], ¤tContactData);
|
|
}
|
|
else
|
|
{
|
|
codeplugContactGetDataForIndex(currentChannelData->contact, ¤tContactData);
|
|
}
|
|
|
|
trxTalkGroupOrPcId = codeplugContactGetPackedId(¤tContactData);
|
|
|
|
tsSetContactHasBeenOverriden(((Channel_t)nonVolatileSettings.currentVFONumber), false);
|
|
|
|
trxUpdateTsForCurrentChannelWithSpecifiedContact(¤tContactData);
|
|
}
|
|
lastHeardClearLastID();
|
|
menuPrivateCallClear();
|
|
}
|
|
|
|
static void setCurrentFreqToScanLimits(void)
|
|
{
|
|
if((currentChannelData->rxFreq < nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber]) ||
|
|
(currentChannelData->rxFreq > nonVolatileSettings.vfoScanHigh[nonVolatileSettings.currentVFONumber])) //if we are not already inside the Low and High Limit freqs then move to the low limit.
|
|
{
|
|
int offset = currentChannelData->txFreq - currentChannelData->rxFreq;
|
|
|
|
currentChannelData->rxFreq = nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber];
|
|
currentChannelData->txFreq = currentChannelData->rxFreq + offset;
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_OR_VFO_FREQ, PROMPT_THRESHOLD_3);
|
|
}
|
|
}
|
|
|
|
void uiVFOSweepScanModePause(bool pause, bool forceDigitalOnPause)
|
|
{
|
|
if (pause)
|
|
{
|
|
uiDataGlobal.Scan.state = SCAN_STATE_PAUSED;
|
|
vfoSweepSavedBandwidth = trxGetBandwidthIs25kHz();
|
|
if (forceDigitalOnPause)
|
|
{
|
|
trxSetModeAndBandwidth(RADIO_MODE_DIGITAL, false);
|
|
}
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
vfoSweepUpdateSamples(0, true, 0);// Force redraw to get rid of the cursor (perhaps we should draw it in the middle);
|
|
}
|
|
else
|
|
{
|
|
trxTerminateCheckAnalogSquelch(RADIO_DEVICE_PRIMARY);
|
|
trxSetModeAndBandwidth(currentChannelData->chMode, vfoSweepSavedBandwidth);
|
|
uiDataGlobal.Scan.state = SCAN_STATE_SCANNING;
|
|
LedWrite(LED_GREEN, 0);
|
|
LedWrite(LED_RED, 0);
|
|
vfoSweepUpdateSamples(0, true, 0);
|
|
headerRowIsDirty = true;
|
|
}
|
|
}
|
|
|
|
static void sweepScanInit(void)
|
|
{
|
|
trxTerminateCheckAnalogSquelch(RADIO_DEVICE_PRIMARY);
|
|
|
|
uiDataGlobal.Scan.active = true;
|
|
uiDataGlobal.Scan.state = SCAN_STATE_SCANNING;
|
|
uiDataGlobal.Scan.scanType = SCAN_TYPE_NORMAL_STEP;
|
|
|
|
uiDataGlobal.VoicePrompts.inhibitInitial = true;
|
|
|
|
if (voicePromptsIsPlaying())
|
|
{
|
|
voicePromptsTerminate();
|
|
}
|
|
|
|
if (nonVolatileSettings.audioPromptMode >= AUDIO_PROMPT_MODE_VOICE_LEVEL_2)
|
|
{
|
|
voicePromptsInit();
|
|
voicePromptsAppendPrompt(PROMPT_SWEEP_SCAN_MODE);
|
|
voicePromptsPlay();
|
|
}
|
|
|
|
uiDataGlobal.Scan.sweepStepSizeIndex = ((nonVolatileSettings.vfoSweepSettings >> 12) & 0x7);
|
|
vfoSweepRssiNoiseFloor = ((nonVolatileSettings.vfoSweepSettings >> 7) & 0x1F);
|
|
vfoSweepGain = (nonVolatileSettings.vfoSweepSettings & 0x7F);
|
|
|
|
screenOperationMode[nonVolatileSettings.currentVFONumber] = VFO_SCREEN_OPERATION_SWEEP;
|
|
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
|
|
memset(vfoSweepSamples, 0x00, VFO_SWEEP_NUM_SAMPLES * sizeof(uint8_t));
|
|
|
|
menuSystemPopAllAndDisplaySpecificRootMenu(UI_VFO_MODE, true);
|
|
|
|
vfoSweepUpdateSamples(0, true, 0);
|
|
headerRowIsDirty = true;
|
|
|
|
// trxCheck*Squelch() won't be called while sweeping, blindly turn the
|
|
// green and red LED off, to avoid being lit while scanning.
|
|
LedWrite(LED_GREEN, 0);
|
|
LedWrite(LED_RED, 0);
|
|
}
|
|
|
|
|
|
static void sweepScanStep(void)
|
|
{
|
|
if (uiDataGlobal.Scan.state != SCAN_STATE_SCANNING)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (ticksTimerHasExpired(&uiDataGlobal.Scan.timer))
|
|
{
|
|
ticksTimerStart(&uiDataGlobal.Scan.timer, VFO_SWEEP_STEP_TIME);
|
|
|
|
if (uiDataGlobal.Scan.sweepSampleIndex < VFO_SWEEP_NUM_SAMPLES)
|
|
{
|
|
#if defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017) || defined(PLATFORM_MD9600)
|
|
radioReadRSSIAndNoiseForBand(currentRadioDevice->trxCurrentBand[TRX_RX_FREQ_BAND]);
|
|
#else
|
|
radioReadRSSIAndNoise();
|
|
#endif
|
|
|
|
vfoSweepSamples[uiDataGlobal.Scan.sweepSampleIndex] = radioDevices[RADIO_DEVICE_PRIMARY].trxRxSignal;// Need to save the samples so for when the freq is changed and we need to scroll the display
|
|
|
|
vfoSweepDrawSample(uiDataGlobal.Scan.sweepSampleIndex);
|
|
|
|
uiDataGlobal.Scan.sweepSampleIndex += uiDataGlobal.Scan.sweepSampleIndexIncrement;
|
|
|
|
displayThemeApply(THEME_ITEM_FG_RX_FREQ, THEME_ITEM_BG);
|
|
displayDrawFastVLine((uiDataGlobal.Scan.sweepSampleIndex) % VFO_SWEEP_NUM_SAMPLES, VFO_SWEEP_GRAPH_START_Y, VFO_SWEEP_GRAPH_HEIGHT_Y, true);// draw solid line in the next location
|
|
displayDrawFastVLine((uiDataGlobal.Scan.sweepSampleIndex + uiDataGlobal.Scan.sweepSampleIndexIncrement) % VFO_SWEEP_NUM_SAMPLES, VFO_SWEEP_GRAPH_START_Y, VFO_SWEEP_GRAPH_HEIGHT_Y, true);// draw solid line in the next location
|
|
displayThemeResetToDefault();
|
|
|
|
if (uiNotificationIsVisible())
|
|
{
|
|
displayRender();
|
|
}
|
|
else
|
|
{
|
|
displayRenderRows(1, ((8 + VFO_SWEEP_GRAPH_HEIGHT_Y) / 8) + 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uiDataGlobal.Scan.sweepSampleIndex = 0;
|
|
uiDataGlobal.Scan.sweepSampleIndexIncrement = 1;// go back to normal increment at the end of the special sweep step used just after the graph is zoomed in
|
|
}
|
|
|
|
uiDataGlobal.Scan.scanSweepCurrentFreq = currentChannelData->rxFreq +
|
|
(VFO_SWEEP_SCAN_RANGE_SAMPLE_STEP_TABLE[uiDataGlobal.Scan.sweepStepSizeIndex] *
|
|
(uiDataGlobal.Scan.sweepSampleIndex -
|
|
#if defined(PLATFORM_MD380) || defined(PLATFORM_MDUV380) || defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017)
|
|
(VFO_SWEEP_NUM_SAMPLES / 2)
|
|
#else
|
|
64
|
|
#endif
|
|
)) / VFO_SWEEP_PIXELS_PER_STEP;
|
|
|
|
trxSetFrequency(uiDataGlobal.Scan.scanSweepCurrentFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
}
|
|
}
|
|
|
|
static void scanInit(void)
|
|
{
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_DUAL_SCAN)
|
|
{
|
|
return;
|
|
}
|
|
|
|
uiDataGlobal.Scan.stepTimeMilliseconds = settingsGetScanStepTimeMilliseconds();
|
|
|
|
// In DIGITAL mode, we need at least 120ms to see the HR-C6000 to start the TS ISR.
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
int dwellTime;
|
|
if(uiDataGlobal.Scan.stepTimeMilliseconds > 150) // if >150ms use DMR Slow mode
|
|
{
|
|
dwellTime = ((currentRadioDevice->trxDMRModeRx == DMR_MODE_DMO) ? SCAN_DMR_SIMPLEX_SLOW_MIN_DWELL_TIME : SCAN_DMR_DUPLEX_SLOW_MIN_DWELL_TIME);
|
|
}
|
|
else
|
|
{
|
|
dwellTime = ((currentRadioDevice->trxDMRModeRx == DMR_MODE_DMO) ? SCAN_DMR_SIMPLEX_FAST_MIN_DWELL_TIME : SCAN_DMR_DUPLEX_FAST_MIN_DWELL_TIME);
|
|
}
|
|
|
|
uiDataGlobal.Scan.dwellTime = ((uiDataGlobal.Scan.stepTimeMilliseconds < dwellTime) ? dwellTime : uiDataGlobal.Scan.stepTimeMilliseconds);
|
|
}
|
|
else
|
|
{
|
|
uiDataGlobal.Scan.dwellTime = uiDataGlobal.Scan.stepTimeMilliseconds;
|
|
}
|
|
uiDataGlobal.Scan.scanType = SCAN_TYPE_NORMAL_STEP;
|
|
|
|
screenOperationMode[nonVolatileSettings.currentVFONumber] = VFO_SCREEN_OPERATION_SCAN;
|
|
uiDataGlobal.Scan.direction = 1;
|
|
|
|
// If scan limits have not been defined. Set them to the current Rx freq .. +1MHz
|
|
if ((nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber] == 0) || (nonVolatileSettings.vfoScanHigh[nonVolatileSettings.currentVFONumber] == 0))
|
|
{
|
|
int limitDown = currentChannelData->rxFreq;
|
|
int limitUp = currentChannelData->rxFreq + 100000;
|
|
|
|
// If the limitUp in not valid, set it to the next band's minFreq
|
|
if (trxGetBandFromFrequency(limitUp) == FREQUENCY_OUT_OF_BAND)
|
|
{
|
|
int band = trxGetNextOrPrevBandFromFrequency(limitUp, true);
|
|
|
|
if (band != -1)
|
|
{
|
|
limitUp = RADIO_HARDWARE_FREQUENCY_BANDS[band].minFreq;
|
|
}
|
|
}
|
|
|
|
settingsSet(nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber], SAFE_MIN(limitUp, limitDown));
|
|
settingsSet(nonVolatileSettings.vfoScanHigh[nonVolatileSettings.currentVFONumber], SAFE_MAX(limitUp, limitDown));
|
|
}
|
|
|
|
// Refresh on every step if scan boundaries is equal to one frequency step.
|
|
uiDataGlobal.Scan.refreshOnEveryStep = ((nonVolatileSettings.vfoScanHigh[nonVolatileSettings.currentVFONumber] - nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber]) <= VFO_FREQ_STEP_TABLE[(currentChannelData->VFOflag5 >> 4)]);
|
|
|
|
clearNuisance();
|
|
|
|
selectedFreq = VFO_SELECTED_FREQUENCY_INPUT_RX;
|
|
|
|
uiDataGlobal.Scan.timer.timeout = 500;
|
|
uiDataGlobal.Scan.state = SCAN_STATE_SCANNING;
|
|
|
|
nextKeyBeepMelody = (int16_t *)MELODY_ACK_BEEP;// Indicate via beep that something different had happened
|
|
|
|
menuSystemPopAllAndDisplaySpecificRootMenu(UI_VFO_MODE, true);
|
|
}
|
|
|
|
static void scanning(void)
|
|
{
|
|
static bool scanPaused = false;
|
|
static bool voicePromptsAnnounced = true;
|
|
|
|
if (!rxPowerSavingIsRxOn())
|
|
{
|
|
uiDataGlobal.Scan.dwellTime = 10000;
|
|
uiDataGlobal.Scan.timer.timeout = 0;
|
|
return;
|
|
}
|
|
|
|
//After initial settling time
|
|
if((uiDataGlobal.Scan.state == SCAN_STATE_SCANNING) && (uiDataGlobal.Scan.timer.timeout > SCAN_SKIP_CHANNEL_INTERVAL) && (uiDataGlobal.Scan.timer.timeout < (uiDataGlobal.Scan.dwellTime - SCAN_FREQ_CHANGE_SETTLING_INTERVAL)))
|
|
{
|
|
// Test for presence of RF Carrier.
|
|
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
if(uiDataGlobal.Scan.stepTimeMilliseconds > 150) // if >150ms use DMR Slow mode
|
|
{
|
|
//DMR Slow MOde
|
|
if (((nonVolatileSettings.dmrCcTsFilter & DMR_TS_FILTER_PATTERN)
|
|
&&
|
|
((slotState != DMR_STATE_IDLE) && ((dmrMonitorCapturedTS != -1) &&
|
|
(((currentRadioDevice->trxDMRModeRx == DMR_MODE_DMO) && (dmrMonitorCapturedTS == trxGetDMRTimeSlot())) ||
|
|
(currentRadioDevice->trxDMRModeRx == DMR_MODE_RMO)))))
|
|
||
|
|
// As soon as the HRC6000 get sync, timeCode != -1 or TS ISR is running
|
|
HRC6000HasGotSync())
|
|
{
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_OR_VFO_FREQ,
|
|
((nonVolatileSettings.scanModePause == SCAN_MODE_STOP) ? AUDIO_PROMPT_MODE_VOICE_LEVEL_3 : PROMPT_THRESHOLD_NEVER_PLAY_IMMEDIATELY));
|
|
|
|
|
|
#if defined(PLATFORM_MD9600)
|
|
uiDataGlobal.Scan.clickDiscriminator = CLICK_DISCRIMINATOR;
|
|
#endif
|
|
uiDataGlobal.Scan.state = SCAN_STATE_SHORT_PAUSED;
|
|
uiDataGlobal.Scan.timer.timeout = ((TIMESLOT_DURATION * 12) + TIMESLOT_DURATION) * 4; // (1 superframe + 1 TS) * 4 = TS Sync + incoming audio
|
|
scanPaused = true;
|
|
voicePromptsAnnounced = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//DMR Fast Mode
|
|
if(trxCarrierDetected(RADIO_DEVICE_PRIMARY))
|
|
{
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_OR_VFO_FREQ,
|
|
((nonVolatileSettings.scanModePause == SCAN_MODE_STOP) ? AUDIO_PROMPT_MODE_VOICE_LEVEL_3 : PROMPT_THRESHOLD_NEVER_PLAY_IMMEDIATELY));
|
|
|
|
if ((nonVolatileSettings.dmrCcTsFilter & DMR_TS_FILTER_PATTERN) == 0)
|
|
{
|
|
uiDataGlobal.Scan.timer.timeout = SCAN_SHORT_PAUSE_TIME * 2; //needs longer delay if in DMR mode and TS Filter is off to allow full detection of signal
|
|
}
|
|
else
|
|
{
|
|
uiDataGlobal.Scan.timer.timeout = SCAN_SHORT_PAUSE_TIME; //start short delay to allow full detection of signal
|
|
}
|
|
|
|
uiDataGlobal.Scan.state = SCAN_STATE_SHORT_PAUSED; //state 1 = pause and test for valid signal that produces audio
|
|
scanPaused = true;
|
|
voicePromptsAnnounced = false;
|
|
#if defined(PLATFORM_MD9600)
|
|
uiDataGlobal.Scan.clickDiscriminator = CLICK_DISCRIMINATOR;
|
|
#endif
|
|
|
|
// Force screen redraw in Analog mode, Dual Watch scanning
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_DUAL_SCAN)
|
|
{
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(trxCarrierDetected(RADIO_DEVICE_PRIMARY))
|
|
{
|
|
//FM Mode
|
|
announceItem(PROMPT_SEQUENCE_CHANNEL_NAME_OR_VFO_FREQ,
|
|
((nonVolatileSettings.scanModePause == SCAN_MODE_STOP) ? AUDIO_PROMPT_MODE_VOICE_LEVEL_3 : PROMPT_THRESHOLD_NEVER_PLAY_IMMEDIATELY));
|
|
|
|
if (nonVolatileSettings.scanModePause == SCAN_MODE_STOP)
|
|
{
|
|
uiVFOModeStopScanning();
|
|
// Just update the header (to prevent hidden mode)
|
|
displayClearRows(0, 2, false);
|
|
uiUtilityRenderHeader(false, false, false);
|
|
displayRenderRows(0, 2);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
uiDataGlobal.Scan.timer.timeout = SCAN_SHORT_PAUSE_TIME; //start short delay to allow full detection of signal
|
|
uiDataGlobal.Scan.state = SCAN_STATE_SHORT_PAUSED; //state 1 = pause and test for valid signal that produces audio
|
|
scanPaused = true;
|
|
voicePromptsAnnounced = false;
|
|
#if defined(PLATFORM_MD9600)
|
|
uiDataGlobal.Scan.clickDiscriminator = CLICK_DISCRIMINATOR;
|
|
#endif
|
|
|
|
// Force screen redraw in Analog mode, Dual Watch scanning
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_DUAL_SCAN)
|
|
{
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Only do this once if scan mode is PAUSE do it every time if scan mode is HOLD
|
|
if(((uiDataGlobal.Scan.state == SCAN_STATE_PAUSED) && (nonVolatileSettings.scanModePause == SCAN_MODE_HOLD)) || (uiDataGlobal.Scan.state == SCAN_STATE_SHORT_PAUSED))
|
|
{
|
|
#if defined(PLATFORM_MD9600)
|
|
if (uiDataGlobal.Scan.clickDiscriminator > 0)
|
|
{
|
|
uiDataGlobal.Scan.clickDiscriminator--;
|
|
}
|
|
else
|
|
{
|
|
#endif
|
|
if (audioAmpGetStatus() & AUDIO_AMP_CHANNEL_RF)
|
|
{
|
|
if (nonVolatileSettings.scanModePause == SCAN_MODE_STOP)
|
|
{
|
|
uiVFOModeStopScanning();
|
|
// Just update the header (to prevent hidden mode)
|
|
displayClearRows(0, 2, false);
|
|
uiUtilityRenderHeader(false, false, false);
|
|
displayRenderRows(0, 2);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
uiDataGlobal.Scan.timer.timeout = nonVolatileSettings.scanDelay * 1000;
|
|
uiDataGlobal.Scan.state = SCAN_STATE_PAUSED;
|
|
}
|
|
|
|
if (voicePromptsAnnounced == false)
|
|
{
|
|
if (nonVolatileSettings.audioPromptMode > AUDIO_PROMPT_MODE_VOICE_LEVEL_2)
|
|
{
|
|
voicePromptsPlay();
|
|
}
|
|
voicePromptsAnnounced = true;
|
|
}
|
|
}
|
|
#if defined(PLATFORM_MD9600)
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if(uiDataGlobal.Scan.timer.timeout > 0)
|
|
{
|
|
uiDataGlobal.Scan.timer.timeout--;
|
|
}
|
|
else
|
|
{
|
|
// We are in Dual Watch scanning mode
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_DUAL_SCAN)
|
|
{
|
|
// Select and set the next VFO
|
|
//
|
|
// Note: nonVolatileSettings.currentVFONumber is not changed using settingsSet(), to prevent crazy EEPROM
|
|
// writes. uiVFOModeStopScanning() is doing this, when the scanning process ends (for any reason).
|
|
nonVolatileSettings.currentVFONumber = (1 - nonVolatileSettings.currentVFONumber);
|
|
currentChannelData = &settingsVFOChannel[nonVolatileSettings.currentVFONumber];
|
|
|
|
currentChannelData->libreDMR_Power = 0x00;// Force channel to the Master power
|
|
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
|
|
//Need to load the Rx group if specified even if TG is currently overridden as we may need it later when the left or right button is pressed
|
|
if (currentChannelData->rxGroupList != 0)
|
|
{
|
|
if (currentChannelData->rxGroupList != lastLoadedRxGroup)
|
|
{
|
|
if (codeplugRxGroupGetDataForIndex(currentChannelData->rxGroupList, ¤tRxGroupData))
|
|
{
|
|
lastLoadedRxGroup = currentChannelData->rxGroupList;
|
|
}
|
|
else
|
|
{
|
|
lastLoadedRxGroup = -1;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memset(¤tRxGroupData, 0xFF, sizeof(CodeplugRxGroup_t));// If the VFO doesnt have an Rx Group ( TG List) the global var needs to be cleared, otherwise it contains the data from the previous screen e.g. Channel screen
|
|
lastLoadedRxGroup = -1;
|
|
}
|
|
|
|
uiVFOModeLoadChannelData(false);
|
|
|
|
// In DIGITAL mode, we need at least 120ms to see the HR-C6000 to start the TS ISR.
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
int dwellTime;
|
|
if(uiDataGlobal.Scan.stepTimeMilliseconds > 150) // if >150ms use DMR Slow mode
|
|
{
|
|
dwellTime = ((currentRadioDevice->trxDMRModeRx == DMR_MODE_DMO) ? SCAN_DMR_SIMPLEX_SLOW_MIN_DWELL_TIME : SCAN_DMR_DUPLEX_SLOW_MIN_DWELL_TIME);
|
|
}
|
|
else
|
|
{
|
|
dwellTime = ((currentRadioDevice->trxDMRModeRx == DMR_MODE_DMO) ? SCAN_DMR_SIMPLEX_FAST_MIN_DWELL_TIME : SCAN_DMR_DUPLEX_FAST_MIN_DWELL_TIME);
|
|
}
|
|
uiDataGlobal.Scan.dwellTime = ((uiDataGlobal.Scan.stepTimeMilliseconds < dwellTime) ? dwellTime : uiDataGlobal.Scan.stepTimeMilliseconds);
|
|
}
|
|
else
|
|
{
|
|
uiDataGlobal.Scan.dwellTime = uiDataGlobal.Scan.stepTimeMilliseconds;
|
|
}
|
|
|
|
if (scanPaused)
|
|
{
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN; // Force screen redraw on scan resume
|
|
scanPaused = false;
|
|
}
|
|
}
|
|
else // Frequency scanning mode
|
|
{
|
|
uiEvent_t tmpEvent = { .buttons = 0, .keys = NO_KEYCODE, .rotary = 0, .function = 0, .events = NO_EVENT, .hasEvent = 0, .time = 0 };
|
|
int fStep = VFO_FREQ_STEP_TABLE[(currentChannelData->VFOflag5 >> 4)];
|
|
|
|
if (uiDataGlobal.Scan.direction == 1)
|
|
{
|
|
if(currentChannelData->rxFreq + fStep <= nonVolatileSettings.vfoScanHigh[nonVolatileSettings.currentVFONumber])
|
|
{
|
|
handleUpKey(&tmpEvent);
|
|
}
|
|
else
|
|
{
|
|
int offset = currentChannelData->txFreq - currentChannelData->rxFreq;
|
|
|
|
currentChannelData->rxFreq = nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber];
|
|
currentChannelData->txFreq = currentChannelData->rxFreq + offset;
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
HRC6000ClearColorCodeSynchronisation();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(currentChannelData->rxFreq + fStep >= nonVolatileSettings.vfoScanLow[nonVolatileSettings.currentVFONumber])
|
|
{
|
|
handleUpKey(&tmpEvent);
|
|
}
|
|
else
|
|
{
|
|
int offset = currentChannelData->txFreq - currentChannelData->rxFreq;
|
|
currentChannelData->rxFreq = nonVolatileSettings.vfoScanHigh[nonVolatileSettings.currentVFONumber];
|
|
currentChannelData->txFreq = currentChannelData->rxFreq+offset;
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, (((currentChannelData->chMode == RADIO_MODE_DIGITAL) && codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_FORCE_DMO)) ? DMR_MODE_DMO : DMR_MODE_AUTO));
|
|
HRC6000ClearColorCodeSynchronisation();
|
|
}
|
|
}
|
|
|
|
if (uiDataGlobal.Scan.refreshOnEveryStep)
|
|
{
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
}
|
|
}
|
|
|
|
uiDataGlobal.Scan.timer.timeout = uiDataGlobal.Scan.dwellTime;
|
|
uiDataGlobal.Scan.state = SCAN_STATE_SCANNING; // Settling and test for carrier presence.
|
|
|
|
if (screenOperationMode[nonVolatileSettings.currentVFONumber] == VFO_SCREEN_OPERATION_SCAN)
|
|
{
|
|
//check all nuisance delete entries and skip channel if there is a match
|
|
for(int i = 0; i < MAX_ZONE_SCAN_NUISANCE_CHANNELS; i++)
|
|
{
|
|
if (uiDataGlobal.Scan.nuisanceDelete[i] == -1)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if(uiDataGlobal.Scan.nuisanceDelete[i] == currentChannelData->rxFreq)
|
|
{
|
|
uiDataGlobal.Scan.timer.timeout = SCAN_SKIP_CHANNEL_INTERVAL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void clearNuisance(void)
|
|
{
|
|
//clear all nuisance delete channels at start of scanning
|
|
for(int i = 0; i < MAX_ZONE_SCAN_NUISANCE_CHANNELS; i++)
|
|
{
|
|
uiDataGlobal.Scan.nuisanceDelete[i] = -1;
|
|
}
|
|
uiDataGlobal.Scan.nuisanceDeleteIndex = 0;
|
|
}
|