741 lines
21 KiB
C
741 lines
21 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 "hardware/HR-C6000.h"
|
|
#include "functions/sound.h"
|
|
#include "user_interface/menuSystem.h"
|
|
#include "user_interface/uiUtilities.h"
|
|
#include "user_interface/uiLocalisation.h"
|
|
#include "interfaces/clockManager.h"
|
|
#include "functions/satellite.h"
|
|
#if defined(HAS_GPS)
|
|
#include "interfaces/gps.h"
|
|
#endif
|
|
#if !defined(PLATFORM_GD77S)
|
|
#include "functions/aprs.h"
|
|
#endif
|
|
|
|
static void updateScreen(void);
|
|
static void handleEvent(uiEvent_t *ev);
|
|
|
|
#define PIT_COUNTS_PER_SECOND 1000U
|
|
|
|
static int timeInSeconds;
|
|
static ticksTimer_t nextSecondTimer = { 0, 0 };
|
|
static bool isShowingLastHeard;
|
|
static bool startBeepPlayed;
|
|
static uint32_t m = 0, micm = 0, mto = 0;
|
|
static bool keepScreenShownOnError = false;
|
|
static bool pttWasReleased = false;
|
|
static bool isTransmittingTone = false;
|
|
static bool isTransmittingDTMF = false;
|
|
static bool aprsPTTBeaconTriggered = false;
|
|
static bool transmitError = false;
|
|
uint32_t xmitErrorTimer = 0;
|
|
static bool isSatelliteScreen;
|
|
|
|
menuStatus_t menuTxScreen(uiEvent_t *ev, bool isFirstRun)
|
|
{
|
|
int radioMode = trxGetMode();
|
|
|
|
if (isFirstRun)
|
|
{
|
|
#if !defined(PLATFORM_GD77S)
|
|
bool isSatelliteAPRS = false;
|
|
#endif
|
|
|
|
monitorModeData.isEnabled = false;
|
|
voicePromptsTerminateNoTail();
|
|
startBeepPlayed = false;
|
|
aprsPTTBeaconTriggered = false;
|
|
transmitError = false;
|
|
uiDataGlobal.Scan.active = false;
|
|
uiDataGlobal.displayChannelSettings = false;
|
|
isTransmittingTone = false;
|
|
isTransmittingDTMF = false;
|
|
isShowingLastHeard = false;
|
|
keepScreenShownOnError = false;
|
|
timeInSeconds = 0;
|
|
pttWasReleased = false;
|
|
xmitErrorTimer = 0;
|
|
isSatelliteScreen = false;
|
|
ticksTimerReset(&nextSecondTimer);
|
|
|
|
if (radioMode == RADIO_MODE_DIGITAL)
|
|
{
|
|
clockManagerSetRunMode(kAPP_PowerModeHsrun, CLOCK_MANAGER_SPEED_HS_RUN);
|
|
}
|
|
else
|
|
{
|
|
isSatelliteScreen = (menuSystemGetPreviousMenuNumber() == MENU_SATELLITE);
|
|
|
|
#if !defined(PLATFORM_GD77S)
|
|
isSatelliteAPRS = (isSatelliteScreen && (currentSatelliteFreqIndex == SATELLITE_APRS_FREQ));
|
|
|
|
if (isSatelliteAPRS && (aprsBeaconingHasSatelliteConfiguration() == false))
|
|
{
|
|
PTTToggledDown = false;
|
|
voxReset();
|
|
menuSystemPopPreviousMenu();
|
|
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if defined(PLATFORM_GD77S)
|
|
uiChannelModeHeartBeatActivityForGD77S(ev); // Dim all lit LEDs
|
|
#endif
|
|
|
|
// If the user was currently entering a new frequency and the PTT get pressed, "leave" that input screen.
|
|
if (uiDataGlobal.FreqEnter.index > 0)
|
|
{
|
|
freqEnterReset();
|
|
updateScreen();
|
|
}
|
|
|
|
if ((codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_RX_ONLY) == 0) && ((nonVolatileSettings.txFreqLimited == BAND_LIMITS_NONE) || trxCheckFrequencyInAmateurBand(currentChannelData->txFreq)
|
|
#if defined(PLATFORM_MD9600)
|
|
|| (codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_OUT_OF_BAND) != 0)
|
|
#endif
|
|
))
|
|
{
|
|
ticksTimerStart(&nextSecondTimer, PIT_COUNTS_PER_SECOND);
|
|
timeInSeconds = currentChannelData->tot * 15;
|
|
|
|
#if defined(HAS_GPS)
|
|
if (SETTINGS_GPS_MODE_GET(nonVolatileSettings) > GPS_MODE_OFF)
|
|
{
|
|
gpsDataInputStartStop(false);
|
|
}
|
|
#endif
|
|
|
|
LedWrite(LED_GREEN, 0);
|
|
LedWrite(LED_RED, 1);
|
|
|
|
HRC6000ClearIsWakingState();
|
|
|
|
if (radioMode == RADIO_MODE_ANALOG)
|
|
{
|
|
trxSetTxCSS(currentChannelData->txTone);
|
|
|
|
if (isSatelliteScreen)
|
|
{
|
|
// Make sure Tx freq is updated before transmission is enabled
|
|
trxSetFrequency(currentChannelData->rxFreq, currentChannelData->txFreq, DMR_MODE_AUTO);
|
|
}
|
|
|
|
trxSetTX();
|
|
|
|
#if !defined(PLATFORM_GD77S)
|
|
if (isSatelliteAPRS)
|
|
{
|
|
// TXDelay
|
|
uint32_t m = ticksGetMillis();
|
|
while((ticksGetMillis() - m) < APRS_XMIT_TX_DELAY)
|
|
{
|
|
vTaskDelay((1U / portTICK_PERIOD_MS));
|
|
}
|
|
|
|
// APRS
|
|
aprsBeaconingSendBeacon(true, false);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
if (settingsLocationIsValid())
|
|
{
|
|
HRC6000SetTalkerAliasLocation(nonVolatileSettings.location.lat, nonVolatileSettings.location.lon);
|
|
}
|
|
|
|
// RADIO_MODE_DIGITAL
|
|
if (!((slotState >= DMR_STATE_REPEATER_WAKE_1) && (slotState <= DMR_STATE_REPEATER_WAKE_3)) )
|
|
{
|
|
trxSetTX();
|
|
}
|
|
}
|
|
|
|
#if !defined(PLATFORM_GD77S)
|
|
if ((isSatelliteScreen == false) || (isSatelliteScreen && (aprsBeaconingIsTransmitting() == false)))
|
|
#endif
|
|
{
|
|
updateScreen();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
menuTxScreenHandleTxTermination(ev, ((codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_RX_ONLY) != 0) ? TXSTOP_RX_ONLY : TXSTOP_OUT_OF_BAND));
|
|
transmitError = true;
|
|
}
|
|
|
|
m = micm = ev->time;
|
|
}
|
|
else
|
|
{
|
|
// Keep displaying the "RX Only" or "Out Of Band" error message
|
|
if (xmitErrorTimer > 0)
|
|
{
|
|
// Wait the voice ends, then count-down 200ms;
|
|
if (nonVolatileSettings.audioPromptMode >= AUDIO_PROMPT_MODE_VOICE_THRESHOLD)
|
|
{
|
|
if (voicePromptsIsPlaying())
|
|
{
|
|
xmitErrorTimer = (20 * 10U);
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
xmitErrorTimer--;
|
|
|
|
if (xmitErrorTimer == 0)
|
|
{
|
|
ev->buttons &= ~BUTTON_PTT; // prevent screen lockout if the operator keeps pressing the PTT button.
|
|
}
|
|
else
|
|
{
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
#if !defined(PLATFORM_GD77S)
|
|
// APRS Beaconing is running
|
|
if (trxTransmissionEnabled)
|
|
{
|
|
// while in satellite mode => no screen redrawing, just waiting for the process ends, the rest is handled in the APRS subsystem.
|
|
if ((isSatelliteScreen && (currentSatelliteFreqIndex == SATELLITE_APRS_FREQ) && aprsBeaconingIsTransmitting()) ||
|
|
// While PTT beaconing is enabled, the rest is handled in the aprs subsystem.
|
|
((isSatelliteScreen == false) && (aprsBeaconingGetMode() == APRS_BEACONING_MODE_PTT) && aprsBeaconingIsTransmitting()))
|
|
{
|
|
if (ticksTimerHasExpired(&nextSecondTimer))
|
|
{
|
|
ticksTimerStart(&nextSecondTimer, PIT_COUNTS_PER_SECOND);
|
|
}
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (trxTransmissionEnabled && ((HRC6000GetIsWakingState() == WAKING_MODE_NONE) || (HRC6000GetIsWakingState() == WAKING_MODE_AWAKEN)))
|
|
{
|
|
if (ticksTimerHasExpired(&nextSecondTimer))
|
|
{
|
|
ticksTimerStart(&nextSecondTimer, PIT_COUNTS_PER_SECOND);
|
|
|
|
if (currentChannelData->tot == 0)
|
|
{
|
|
timeInSeconds++;
|
|
}
|
|
else
|
|
{
|
|
timeInSeconds--;
|
|
if (timeInSeconds <= (nonVolatileSettings.txTimeoutBeepX5Secs * 5))
|
|
{
|
|
if ((timeInSeconds % 5) == 0)
|
|
{
|
|
soundSetMelody(MELODY_KEY_BEEP);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((currentChannelData->tot != 0) && (timeInSeconds == 0))
|
|
{
|
|
menuTxScreenHandleTxTermination(ev, TXSTOP_TIMEOUT);
|
|
keepScreenShownOnError = true;
|
|
}
|
|
else
|
|
{
|
|
if (!isShowingLastHeard)
|
|
{
|
|
updateScreen();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (radioMode == RADIO_MODE_DIGITAL)
|
|
{
|
|
if ((nonVolatileSettings.beepOptions & BEEP_TX_START) &&
|
|
(startBeepPlayed == false) && trxIsTransmitting
|
|
&& (melody_play == NULL))
|
|
{
|
|
startBeepPlayed = true;// set this even if the beep is not actaully played because of the vox, as otherwise this code will get continuously run
|
|
// If VOX is running, don't send a beep as it will reset its the trigger status.
|
|
if ((voxIsEnabled() == false) || (voxIsEnabled() && (voxIsTriggered() == false)))
|
|
{
|
|
soundSetMelody(MELODY_DMR_TX_START_BEEP);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Do not update Mic level on Timeout.
|
|
#if !defined(PLATFORM_GD77S)
|
|
if (aprsBeaconingIsTransmitting() == false)
|
|
{
|
|
if (((((currentChannelData->tot != 0) && (timeInSeconds == 0)) == false) && (ev->time - micm) > 100))
|
|
{
|
|
if (radioMode == RADIO_MODE_DIGITAL)
|
|
{
|
|
uiUtilityDrawDMRMicLevelBarGraph();
|
|
}
|
|
else
|
|
{
|
|
uiUtilityDrawFMMicLevelBarGraph();
|
|
}
|
|
|
|
displayRenderRows(1, 2);;//return;// don't do anything as it will affect the transmission !
|
|
|
|
micm = ev->time;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Timeout happened, postpone going further otherwise timeout
|
|
// screen won't be visible at all.
|
|
if (((currentChannelData->tot != 0) && (timeInSeconds == 0)) || keepScreenShownOnError)
|
|
{
|
|
// Wait the voice ends, then count-down 500ms;
|
|
if (nonVolatileSettings.audioPromptMode >= AUDIO_PROMPT_MODE_VOICE_THRESHOLD)
|
|
{
|
|
if (voicePromptsIsPlaying())
|
|
{
|
|
mto = ev->time;
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if ((ev->time - mto) < 500)
|
|
{
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
|
|
keepScreenShownOnError = false;
|
|
ev->buttons &= ~BUTTON_PTT; // prevent screen lockout if the operator keeps pressing the PTT button.
|
|
}
|
|
|
|
// Once the PTT key has been released, it is forbidden to re-key before the whole TX chain
|
|
// has finished (see the first statement in handleEvent())
|
|
// That's important in DMR mode, otherwise quickly press/release the PTT key will left
|
|
// the system in an unexpected state (RED led on, displayed TXScreen, but PA off).
|
|
// It doesn't have any impact on FM mode.
|
|
if (((ev->buttons & BUTTON_PTT) == 0) && (pttWasReleased == false))
|
|
{
|
|
pttWasReleased = true;
|
|
}
|
|
|
|
if ((ev->buttons & BUTTON_PTT) && pttWasReleased)
|
|
{
|
|
ev->buttons &= ~BUTTON_PTT;
|
|
}
|
|
|
|
// Got an event, or
|
|
if (ev->hasEvent || // PTT released, Timeout triggered,
|
|
( (((ev->buttons & BUTTON_PTT) == 0) || ((currentChannelData->tot != 0) && (timeInSeconds == 0))) ||
|
|
// or waiting for DMR ending (meanwhile, updating every 100ms)
|
|
((trxTransmissionEnabled == false) && ((ev->time - m) > 100))))
|
|
{
|
|
handleEvent(ev);
|
|
m = ev->time;
|
|
}
|
|
else
|
|
{
|
|
if ((HRC6000GetIsWakingState() == WAKING_MODE_FAILED) && trxTransmissionEnabled)
|
|
{
|
|
trxTransmissionEnabled = false;
|
|
menuTxScreenHandleTxTermination(ev, TXSTOP_TIMEOUT);
|
|
keepScreenShownOnError = true;
|
|
}
|
|
}
|
|
}
|
|
return MENU_STATUS_SUCCESS;
|
|
}
|
|
|
|
bool menuTxScreenDisplaysLastHeard(void)
|
|
{
|
|
return isShowingLastHeard;
|
|
}
|
|
|
|
static void updateScreen(void)
|
|
{
|
|
#if !defined(PLATFORM_GD77S)
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN;
|
|
|
|
if (isSatelliteScreen)
|
|
{
|
|
menuSatelliteTxScreen(timeInSeconds);
|
|
}
|
|
else
|
|
{
|
|
if (menuSystemGetRootMenuNumber() == UI_VFO_MODE)
|
|
{
|
|
uiVFOModeUpdateScreen(timeInSeconds);
|
|
}
|
|
else
|
|
{
|
|
uiChannelModeUpdateScreen(timeInSeconds);
|
|
}
|
|
}
|
|
|
|
if (nonVolatileSettings.backlightMode != BACKLIGHT_MODE_BUTTONS)
|
|
{
|
|
displayLightOverrideTimeout(-1);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void handleEvent(uiEvent_t *ev)
|
|
{
|
|
// Xmiting ends (normal or timeouted)
|
|
if (((ev->buttons & BUTTON_PTT) == 0) || (((currentChannelData->tot != 0) && (timeInSeconds == 0))))
|
|
{
|
|
if (trxTransmissionEnabled)
|
|
{
|
|
#if !defined(PLATFORM_GD77S)
|
|
if ((transmitError == false) &&
|
|
(isSatelliteScreen == false) && (aprsBeaconingGetMode() == APRS_BEACONING_MODE_PTT) && (aprsPTTBeaconTriggered == false))
|
|
{
|
|
aprsPTTBeaconTriggered = true;
|
|
|
|
if (aprsBeaconingSendBeacon(false, false))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
trxTransmissionEnabled = false;
|
|
|
|
if(isTransmittingTone || isTransmittingDTMF)
|
|
{
|
|
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_DM1801) || defined(PLATFORM_DM1801A) || defined(PLATFORM_RD5R)
|
|
trxSelectVoiceChannel(AT1846_VOICE_CHANNEL_MIC);
|
|
audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
|
|
#else
|
|
|
|
#if defined(PLATFORM_MD9600)
|
|
trxDTMFoff(true);
|
|
#else // PLATFORM_MD9600
|
|
if(isTransmittingDTMF)
|
|
{
|
|
trxDTMFoff(true);
|
|
}
|
|
else
|
|
{
|
|
trxSetTone1(0);
|
|
}
|
|
#endif // PLATFORM_MD9600
|
|
|
|
if(soundMelodyIsPlaying())
|
|
{
|
|
soundStopMelody();
|
|
}
|
|
#endif
|
|
isTransmittingTone = false;
|
|
isTransmittingDTMF = false;
|
|
}
|
|
|
|
if (trxGetMode() == RADIO_MODE_ANALOG)
|
|
{
|
|
// In analog mode. Stop transmitting immediately
|
|
LedWrite(LED_RED, 0);
|
|
|
|
#if defined(HAS_GPS)
|
|
if (SETTINGS_GPS_MODE_GET(nonVolatileSettings) > GPS_MODE_OFF)
|
|
{
|
|
gpsDataInputStartStop(true);
|
|
}
|
|
#endif
|
|
|
|
// Need to wrap this in Task Critical to avoid bus contention on the I2C bus.
|
|
trxSetRxCSS(RADIO_DEVICE_PRIMARY, currentChannelData->rxTone);
|
|
trxActivateRx(true);
|
|
trxIsTransmitting = false;
|
|
|
|
menuSystemPopPreviousMenu();
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN; // we need immediate redraw
|
|
}
|
|
else
|
|
{
|
|
HRC6000ClearIsWakingState();
|
|
|
|
if (isShowingLastHeard)
|
|
{
|
|
isShowingLastHeard = false;
|
|
updateScreen();
|
|
}
|
|
}
|
|
// When not in analog mode, only the trxIsTransmitting flag is cleared
|
|
// This screen keeps getting called via the handleEvent function and goes into the else clause - below.
|
|
}
|
|
else
|
|
{
|
|
// In DMR mode, wait for the DMR system to finish before exiting
|
|
if (trxIsTransmitting == false)
|
|
{
|
|
if ((nonVolatileSettings.beepOptions & BEEP_TX_STOP) && (melody_play == NULL) && (HRC6000GetIsWakingState() != WAKING_MODE_FAILED))
|
|
{
|
|
soundSetMelody(MELODY_DMR_TX_STOP_BEEP);
|
|
}
|
|
|
|
LedWrite(LED_RED, 0);
|
|
|
|
#if defined(HAS_GPS)
|
|
if (SETTINGS_GPS_MODE_GET(nonVolatileSettings) > GPS_MODE_OFF)
|
|
{
|
|
gpsDataInputStartStop(true);
|
|
}
|
|
#endif
|
|
|
|
// If there is a signal, lit the Green LED
|
|
if ((LedRead(LED_GREEN) == 0) && (trxCarrierDetected(RADIO_DEVICE_PRIMARY) || (audioAmpGetStatus() & AUDIO_AMP_CHANNEL_RF)))
|
|
{
|
|
LedWrite(LED_GREEN, 1);
|
|
}
|
|
|
|
if (trxGetMode() == RADIO_MODE_DIGITAL)
|
|
{
|
|
clockManagerSetRunMode(kAPP_PowerModeRun, CLOCK_MANAGER_SPEED_RUN);
|
|
}
|
|
|
|
HRC6000ClearIsWakingState();
|
|
|
|
menuSystemPopPreviousMenu();
|
|
uiDataGlobal.displayQSOState = QSO_DISPLAY_DEFAULT_SCREEN; // we need immediate redraw
|
|
}
|
|
}
|
|
|
|
// Waiting for TX to end, no need to go further.
|
|
return;
|
|
}
|
|
|
|
// Key action while xmitting (ANALOG), Tone triggering
|
|
if ((isTransmittingTone == false) && (isTransmittingDTMF == false) &&
|
|
((ev->buttons & BUTTON_PTT) != 0) && trxTransmissionEnabled && (trxGetMode() == RADIO_MODE_ANALOG))
|
|
{
|
|
int keyval = menuGetKeypadKeyValue(ev, false);
|
|
// Send 1750Hz
|
|
if (BUTTONCHECK_DOWN(ev, BUTTON_SK2))
|
|
{
|
|
if (isSatelliteScreen)
|
|
{
|
|
if (satelliteDataNative[uiDataGlobal.SatelliteAndAlarmData.currentSatellite].freqs[currentSatelliteFreqIndex].armCTCSS != 0)
|
|
{
|
|
trxActivateRx(true);
|
|
trxSetTxCSS(satelliteDataNative[uiDataGlobal.SatelliteAndAlarmData.currentSatellite].freqs[currentSatelliteFreqIndex].armCTCSS);
|
|
trxSetTX();
|
|
}
|
|
}
|
|
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_DM1801) || defined(PLATFORM_DM1801A) || defined(PLATFORM_RD5R)
|
|
else
|
|
{
|
|
trxSetTone1(1750);
|
|
trxSelectVoiceChannel(AT1846_VOICE_CHANNEL_TONE1);
|
|
audioAmpEnable(AUDIO_AMP_CHANNEL_RF);
|
|
GPIO_PinWrite(GPIO_RX_audio_mux, Pin_RX_audio_mux, 1);
|
|
isTransmittingTone = true;
|
|
}
|
|
#elif ! defined(PLATFORM_MD9600)
|
|
else
|
|
{ //send 1750
|
|
trxSetTone1(1750);
|
|
soundSetMelody(MELODY_1750);
|
|
isTransmittingTone = true;
|
|
}
|
|
#endif
|
|
}
|
|
#if defined(PLATFORM_MD9600)
|
|
else if ((keyval == 16) && (isSatelliteScreen == false)) // A/B key
|
|
{ //send 1750
|
|
trxSetTone1(1750);
|
|
soundSetMelody(MELODY_1750);
|
|
isTransmittingTone = true;
|
|
}
|
|
#endif
|
|
else
|
|
{ // Send DTMF
|
|
if ((keyval != 99) && (isSatelliteScreen == false))
|
|
{
|
|
trxSetDTMF(keyval);
|
|
isTransmittingDTMF = true;
|
|
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_DM1801) || defined(PLATFORM_DM1801A) || defined(PLATFORM_RD5R)
|
|
trxSelectVoiceChannel(AT1846_VOICE_CHANNEL_DTMF);
|
|
audioAmpEnable(AUDIO_AMP_CHANNEL_RF);
|
|
GPIO_PinWrite(GPIO_RX_audio_mux, Pin_RX_audio_mux, 1);
|
|
#else
|
|
soundSetMelody(MELODY_DTMF);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stop xmitting Tone
|
|
if ((isTransmittingTone || isTransmittingDTMF) && (BUTTONCHECK_DOWN(ev, BUTTON_SK2) == 0) && ((ev->keys.key == 0) || (ev->keys.event & KEY_MOD_UP)))
|
|
{
|
|
if (isSatelliteScreen)
|
|
{
|
|
if (satelliteDataNative[uiDataGlobal.SatelliteAndAlarmData.currentSatellite].freqs[currentSatelliteFreqIndex].armCTCSS != 0)
|
|
{
|
|
trxActivateRx(true);
|
|
trxSetTxCSS(currentChannelData->txTone);
|
|
trxSetTX();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_DM1801) || defined(PLATFORM_DM1801A) || defined(PLATFORM_RD5R)
|
|
trxSelectVoiceChannel(AT1846_VOICE_CHANNEL_MIC);
|
|
audioAmpDisable(AUDIO_AMP_CHANNEL_RF);
|
|
#else
|
|
|
|
#if defined(PLATFORM_MD9600)
|
|
trxDTMFoff(true);
|
|
#else // PLATFORM_MD9600
|
|
if(isTransmittingDTMF)
|
|
{
|
|
trxDTMFoff(true);
|
|
}
|
|
else
|
|
{
|
|
trxSetTone1(0);
|
|
}
|
|
#endif // PLATFORM_MD9600
|
|
|
|
if(soundMelodyIsPlaying())
|
|
{
|
|
soundStopMelody();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
isTransmittingTone = false;
|
|
isTransmittingDTMF = false;
|
|
}
|
|
|
|
#if !defined(PLATFORM_GD77S)
|
|
if ((trxGetMode() == RADIO_MODE_DIGITAL) && BUTTONCHECK_SHORTUP(ev, BUTTON_SK1) && trxTransmissionEnabled)
|
|
{
|
|
isShowingLastHeard = !isShowingLastHeard;
|
|
if (isShowingLastHeard)
|
|
{
|
|
menuLastHeardInit();
|
|
menuLastHeardUpdateScreen(false, false, false);
|
|
}
|
|
else
|
|
{
|
|
updateScreen();
|
|
}
|
|
}
|
|
|
|
// Forward key events to LH screen, if shown
|
|
if (isShowingLastHeard && (ev->events & KEY_EVENT))
|
|
{
|
|
menuLastHeardHandleEvent(ev);
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
void menuTxScreenHandleTxTermination(uiEvent_t *ev, txTerminationReason_t reason)
|
|
{
|
|
PTTToggledDown = false;
|
|
voxReset();
|
|
|
|
voicePromptsTerminateNoTail();
|
|
voicePromptsInit();
|
|
|
|
#if !defined(PLATFORM_GD77S)
|
|
displayClearBuf();
|
|
displayThemeApply(THEME_ITEM_FG_DECORATION, THEME_ITEM_BG_NOTIFICATION);
|
|
displayDrawRoundRectWithDropShadow(4, 4, 120 + DISPLAY_H_EXTRA_PIXELS, (DISPLAY_SIZE_Y - 6), 5, true);
|
|
#endif
|
|
|
|
switch (reason)
|
|
{
|
|
case TXSTOP_RX_ONLY:
|
|
case TXSTOP_OUT_OF_BAND:
|
|
#if !defined(PLATFORM_GD77S)
|
|
displayThemeApply(THEME_ITEM_FG_ERROR_NOTIFICATION, THEME_ITEM_BG_NOTIFICATION);
|
|
displayPrintCentered(4 + (DISPLAY_V_EXTRA_PIXELS / 4), currentLanguage->error, FONT_SIZE_4);
|
|
#endif
|
|
|
|
voicePromptsAppendLanguageString(currentLanguage->error);
|
|
voicePromptsAppendPrompt(PROMPT_SILENCE);
|
|
|
|
if (codeplugChannelGetFlag(currentChannelData, CHANNEL_FLAG_RX_ONLY) != 0)
|
|
{
|
|
#if !defined(PLATFORM_GD77S)
|
|
displayPrintCentered((DISPLAY_SIZE_Y - 24) - (DISPLAY_V_EXTRA_PIXELS / 4), currentLanguage->rx_only, FONT_SIZE_3);
|
|
#endif
|
|
voicePromptsAppendLanguageString(currentLanguage->rx_only);
|
|
}
|
|
else
|
|
{
|
|
#if !defined(PLATFORM_GD77S)
|
|
displayPrintCentered((DISPLAY_SIZE_Y - 24) - (DISPLAY_V_EXTRA_PIXELS / 4), currentLanguage->out_of_band, FONT_SIZE_3);
|
|
#endif
|
|
voicePromptsAppendLanguageString(currentLanguage->out_of_band);
|
|
}
|
|
xmitErrorTimer = (100 * 10U);
|
|
break;
|
|
|
|
case TXSTOP_TIMEOUT:
|
|
#if !defined(PLATFORM_GD77S)
|
|
displayThemeApply(THEME_ITEM_FG_WARNING_NOTIFICATION, THEME_ITEM_BG_NOTIFICATION);
|
|
displayPrintCentered(((DISPLAY_SIZE_Y - FONT_SIZE_4_HEIGHT) / 2), currentLanguage->timeout, FONT_SIZE_4);
|
|
#endif
|
|
|
|
// From G4EML commit:
|
|
// Timeout Voice Prompt doesn't work on DMR. It actually sends a distorted version of the prompt on the transmission.
|
|
// Presumably because the codec cant handle encoding and decoding at the same time.
|
|
#if defined(PLATFORM_GD77) || defined(PLATFORM_GD77S) || defined(PLATFORM_DM1801) || defined(PLATFORM_DM1801A) || defined(PLATFORM_RD5R)
|
|
voicePromptsAppendLanguageString(currentLanguage->timeout);
|
|
#endif
|
|
|
|
if (menuSystemGetCurrentMenuNumber() == UI_TX_SCREEN)
|
|
{
|
|
mto = ev->time;
|
|
}
|
|
break;
|
|
}
|
|
|
|
#if !defined(PLATFORM_GD77S)
|
|
displayThemeResetToDefault();
|
|
displayRender();
|
|
displayLightOverrideTimeout(-1);
|
|
#endif
|
|
|
|
if ((nonVolatileSettings.audioPromptMode < AUDIO_PROMPT_MODE_VOICE_THRESHOLD) || (reason == TXSTOP_TIMEOUT))
|
|
{
|
|
soundSetMelody((reason == TXSTOP_TIMEOUT) ? MELODY_TX_TIMEOUT_BEEP : MELODY_ERROR_BEEP);
|
|
}
|
|
else
|
|
{
|
|
voicePromptsPlay();
|
|
}
|
|
}
|