mirror of
https://github.com/phdlee/uvk5cec
synced 2024-11-21 23:51:47 +00:00
528 lines
13 KiB
C
528 lines
13 KiB
C
/* Copyright 2023 Dual Tachyon
|
|
* https://github.com/DualTachyon
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "app/app.h"
|
|
#include "app/dtmf.h"
|
|
#include "app/generic.h"
|
|
#include "app/menu.h"
|
|
#include "app/scanner.h"
|
|
#include "audio.h"
|
|
#include "driver/bk4819.h"
|
|
#include "frequencies.h"
|
|
#include "misc.h"
|
|
#include "radio.h"
|
|
#include "settings.h"
|
|
#include "ui/inputbox.h"
|
|
#include "ui/ui.h"
|
|
|
|
DCS_CodeType_t gScanCssResultType;
|
|
uint8_t gScanCssResultCode;
|
|
bool gScanSingleFrequency; // scan CTCSS/DCS codes for current frequency
|
|
SCAN_SaveState_t gScannerSaveState;
|
|
uint8_t gScanChannel;
|
|
uint32_t gScanFrequency;
|
|
SCAN_CssState_t gScanCssState;
|
|
uint8_t gScanProgressIndicator;
|
|
bool gScanUseCssResult;
|
|
|
|
STEP_Setting_t stepSetting;
|
|
uint8_t scanHitCount;
|
|
|
|
|
|
static void SCANNER_Key_DIGITS(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
|
{
|
|
if (!bKeyHeld && bKeyPressed)
|
|
{
|
|
if (gScannerSaveState == SCAN_SAVE_CHAN_SEL) {
|
|
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
|
|
|
INPUTBOX_Append(Key);
|
|
|
|
gRequestDisplayScreen = DISPLAY_SCANNER;
|
|
|
|
if (gInputBoxIndex < 3) {
|
|
#ifdef ENABLE_VOICE
|
|
gAnotherVoiceID = (VOICE_ID_t)Key;
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
gInputBoxIndex = 0;
|
|
|
|
uint16_t chan = ((gInputBox[0] * 100) + (gInputBox[1] * 10) + gInputBox[2]) - 1;
|
|
if (IS_MR_CHANNEL(chan)) {
|
|
#ifdef ENABLE_VOICE
|
|
gAnotherVoiceID = (VOICE_ID_t)Key;
|
|
#endif
|
|
gShowChPrefix = RADIO_CheckValidChannel(chan, false, 0);
|
|
gScanChannel = (uint8_t)chan;
|
|
return;
|
|
}
|
|
}
|
|
|
|
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
|
}
|
|
}
|
|
|
|
static void SCANNER_Key_EXIT(bool bKeyPressed, bool bKeyHeld)
|
|
{
|
|
if (!bKeyHeld && bKeyPressed) { // short pressed
|
|
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
|
|
|
switch (gScannerSaveState) {
|
|
case SCAN_SAVE_NO_PROMPT:
|
|
SCANNER_Stop();
|
|
gRequestDisplayScreen = DISPLAY_MAIN;
|
|
break;
|
|
|
|
case SCAN_SAVE_CHAN_SEL:
|
|
if (gInputBoxIndex > 0) {
|
|
gInputBox[--gInputBoxIndex] = 10;
|
|
gRequestDisplayScreen = DISPLAY_SCANNER;
|
|
break;
|
|
}
|
|
|
|
// Fallthrough
|
|
|
|
case SCAN_SAVE_CHANNEL:
|
|
gScannerSaveState = SCAN_SAVE_NO_PROMPT;
|
|
#ifdef ENABLE_VOICE
|
|
gAnotherVoiceID = VOICE_ID_CANCEL;
|
|
#endif
|
|
gRequestDisplayScreen = DISPLAY_SCANNER;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void SCANNER_Key_MENU(bool bKeyPressed, bool bKeyHeld)
|
|
{
|
|
if (bKeyHeld || !bKeyPressed) // ignore long press or release button events
|
|
return;
|
|
|
|
if (gScanCssState == SCAN_CSS_STATE_OFF && !gScanSingleFrequency) {
|
|
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
|
return;
|
|
}
|
|
|
|
if (gScanCssState == SCAN_CSS_STATE_SCANNING && gScanSingleFrequency) {
|
|
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
|
return;
|
|
}
|
|
|
|
if (gScanCssState == SCAN_CSS_STATE_FAILED) {
|
|
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
|
return;
|
|
}
|
|
|
|
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
|
|
|
switch (gScannerSaveState) {
|
|
case SCAN_SAVE_NO_PROMPT:
|
|
if (!gScanSingleFrequency)
|
|
{
|
|
uint32_t freq250 = FREQUENCY_RoundToStep(gScanFrequency, 250);
|
|
uint32_t freq625 = FREQUENCY_RoundToStep(gScanFrequency, 625);
|
|
|
|
uint32_t diff250 = gScanFrequency > freq250 ? gScanFrequency - freq250 : freq250 - gScanFrequency;
|
|
uint32_t diff625 = gScanFrequency > freq625 ? gScanFrequency - freq625 : freq625 - gScanFrequency;
|
|
|
|
if(diff250 > diff625) {
|
|
stepSetting = STEP_6_25kHz;
|
|
gScanFrequency = freq625;
|
|
}
|
|
else {
|
|
stepSetting = STEP_2_5kHz;
|
|
gScanFrequency = freq250;
|
|
}
|
|
}
|
|
|
|
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
|
|
gScannerSaveState = SCAN_SAVE_CHAN_SEL;
|
|
gScanChannel = gTxVfo->CHANNEL_SAVE;
|
|
gShowChPrefix = RADIO_CheckValidChannel(gTxVfo->CHANNEL_SAVE, false, 0);
|
|
}
|
|
else {
|
|
gScannerSaveState = SCAN_SAVE_CHANNEL;
|
|
}
|
|
|
|
gScanCssState = SCAN_CSS_STATE_FOUND;
|
|
#ifdef ENABLE_VOICE
|
|
gAnotherVoiceID = VOICE_ID_MEMORY_CHANNEL;
|
|
#endif
|
|
gRequestDisplayScreen = DISPLAY_SCANNER;
|
|
|
|
gUpdateStatus = true;
|
|
break;
|
|
|
|
case SCAN_SAVE_CHAN_SEL:
|
|
if (gInputBoxIndex == 0) {
|
|
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
|
gRequestDisplayScreen = DISPLAY_SCANNER;
|
|
gScannerSaveState = SCAN_SAVE_CHANNEL;
|
|
}
|
|
break;
|
|
|
|
case SCAN_SAVE_CHANNEL:
|
|
if (!gScanSingleFrequency) {
|
|
RADIO_InitInfo(gTxVfo, gTxVfo->CHANNEL_SAVE, gScanFrequency);
|
|
|
|
if (gScanUseCssResult) {
|
|
gTxVfo->freq_config_RX.CodeType = gScanCssResultType;
|
|
gTxVfo->freq_config_RX.Code = gScanCssResultCode;
|
|
}
|
|
|
|
gTxVfo->freq_config_TX = gTxVfo->freq_config_RX;
|
|
gTxVfo->STEP_SETTING = stepSetting;
|
|
}
|
|
else {
|
|
RADIO_ConfigureChannel(0, VFO_CONFIGURE_RELOAD);
|
|
RADIO_ConfigureChannel(1, VFO_CONFIGURE_RELOAD);
|
|
|
|
gTxVfo->freq_config_RX.CodeType = gScanCssResultType;
|
|
gTxVfo->freq_config_RX.Code = gScanCssResultCode;
|
|
gTxVfo->freq_config_TX.CodeType = gScanCssResultType;
|
|
gTxVfo->freq_config_TX.Code = gScanCssResultCode;
|
|
}
|
|
|
|
uint8_t chan;
|
|
if (IS_MR_CHANNEL(gTxVfo->CHANNEL_SAVE)) {
|
|
chan = gScanChannel;
|
|
gEeprom.MrChannel[gEeprom.TX_VFO] = chan;
|
|
}
|
|
else {
|
|
chan = gTxVfo->Band + FREQ_CHANNEL_FIRST;
|
|
gEeprom.FreqChannel[gEeprom.TX_VFO] = chan;
|
|
}
|
|
|
|
gTxVfo->CHANNEL_SAVE = chan;
|
|
gEeprom.ScreenChannel[gEeprom.TX_VFO] = chan;
|
|
#ifdef ENABLE_VOICE
|
|
gAnotherVoiceID = VOICE_ID_CONFIRM;
|
|
#endif
|
|
gRequestDisplayScreen = DISPLAY_SCANNER;
|
|
gRequestSaveChannel = 2;
|
|
gScannerSaveState = SCAN_SAVE_NO_PROMPT;
|
|
break;
|
|
|
|
default:
|
|
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void SCANNER_Key_STAR(bool bKeyPressed, bool bKeyHeld)
|
|
{
|
|
if (!bKeyHeld && bKeyPressed) {
|
|
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
|
SCANNER_Start(gScanSingleFrequency);
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void SCANNER_Key_UP_DOWN(bool bKeyPressed, bool pKeyHeld, int8_t Direction)
|
|
{
|
|
if (pKeyHeld) {
|
|
if (!bKeyPressed)
|
|
return;
|
|
}
|
|
else {
|
|
if (!bKeyPressed)
|
|
return;
|
|
|
|
gInputBoxIndex = 0;
|
|
gBeepToPlay = BEEP_1KHZ_60MS_OPTIONAL;
|
|
}
|
|
|
|
if (gScannerSaveState == SCAN_SAVE_CHAN_SEL) {
|
|
gScanChannel = NUMBER_AddWithWraparound(gScanChannel, Direction, 0, MR_CHANNEL_LAST);
|
|
gShowChPrefix = RADIO_CheckValidChannel(gScanChannel, false, 0);
|
|
gRequestDisplayScreen = DISPLAY_SCANNER;
|
|
}
|
|
else
|
|
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
|
}
|
|
|
|
void SCANNER_ProcessKeys(KEY_Code_t Key, bool bKeyPressed, bool bKeyHeld)
|
|
{
|
|
switch (Key) {
|
|
case KEY_0:
|
|
case KEY_1:
|
|
case KEY_2:
|
|
case KEY_3:
|
|
case KEY_4:
|
|
case KEY_5:
|
|
case KEY_6:
|
|
case KEY_7:
|
|
case KEY_8:
|
|
case KEY_9:
|
|
SCANNER_Key_DIGITS(Key, bKeyPressed, bKeyHeld);
|
|
break;
|
|
case KEY_MENU:
|
|
SCANNER_Key_MENU(bKeyPressed, bKeyHeld);
|
|
break;
|
|
case KEY_UP:
|
|
SCANNER_Key_UP_DOWN(bKeyPressed, bKeyHeld, 1);
|
|
break;
|
|
case KEY_DOWN:
|
|
SCANNER_Key_UP_DOWN(bKeyPressed, bKeyHeld, -1);
|
|
break;
|
|
case KEY_EXIT:
|
|
SCANNER_Key_EXIT(bKeyPressed, bKeyHeld);
|
|
break;
|
|
case KEY_STAR:
|
|
SCANNER_Key_STAR(bKeyPressed, bKeyHeld);
|
|
break;
|
|
case KEY_PTT:
|
|
GENERIC_Key_PTT(bKeyPressed);
|
|
break;
|
|
default:
|
|
if (!bKeyHeld && bKeyPressed)
|
|
gBeepToPlay = BEEP_500HZ_60MS_DOUBLE_BEEP_OPTIONAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SCANNER_Start(bool singleFreq)
|
|
{
|
|
gScanSingleFrequency = singleFreq;
|
|
gMonitor = false;
|
|
|
|
#ifdef ENABLE_VOICE
|
|
gAnotherVoiceID = VOICE_ID_SCANNING_BEGIN;
|
|
#endif
|
|
|
|
BK4819_StopScan();
|
|
RADIO_SelectVfos();
|
|
|
|
#ifdef ENABLE_NOAA
|
|
if (IS_NOAA_CHANNEL(gRxVfo->CHANNEL_SAVE))
|
|
gRxVfo->CHANNEL_SAVE = FREQ_CHANNEL_FIRST + BAND6_400MHz;
|
|
#endif
|
|
|
|
uint8_t backupStep = gRxVfo->STEP_SETTING;
|
|
uint16_t backupFrequency = gRxVfo->StepFrequency;
|
|
|
|
RADIO_InitInfo(gRxVfo, gRxVfo->CHANNEL_SAVE, gRxVfo->pRX->Frequency);
|
|
|
|
gRxVfo->STEP_SETTING = backupStep;
|
|
gRxVfo->StepFrequency = backupFrequency;
|
|
|
|
RADIO_SetupRegisters(true);
|
|
|
|
#ifdef ENABLE_NOAA
|
|
gIsNoaaMode = false;
|
|
#endif
|
|
|
|
if (gScanSingleFrequency) {
|
|
gScanCssState = SCAN_CSS_STATE_SCANNING;
|
|
gScanFrequency = gRxVfo->pRX->Frequency;
|
|
stepSetting = gRxVfo->STEP_SETTING;
|
|
|
|
BK4819_PickRXFilterPathBasedOnFrequency(gScanFrequency);
|
|
BK4819_SetScanFrequency(gScanFrequency);
|
|
|
|
gUpdateStatus = true;
|
|
}
|
|
else {
|
|
gScanCssState = SCAN_CSS_STATE_OFF;
|
|
gScanFrequency = 0xFFFFFFFF;
|
|
|
|
BK4819_PickRXFilterPathBasedOnFrequency(gScanFrequency);
|
|
BK4819_EnableFrequencyScan();
|
|
|
|
gUpdateStatus = true;
|
|
}
|
|
|
|
#ifdef ENABLE_DTMF_CALLING
|
|
DTMF_clear_RX();
|
|
#endif
|
|
|
|
gScanDelay_10ms = scan_delay_10ms;
|
|
gScanCssResultCode = 0xFF;
|
|
gScanCssResultType = 0xFF;
|
|
scanHitCount = 0;
|
|
gScanUseCssResult = false;
|
|
g_CxCSS_TAIL_Found = false;
|
|
g_CDCSS_Lost = false;
|
|
gCDCSSCodeType = 0;
|
|
g_CTCSS_Lost = false;
|
|
#ifdef ENABLE_VOX
|
|
g_VOX_Lost = false;
|
|
#endif
|
|
g_SquelchLost = false;
|
|
gScannerSaveState = SCAN_SAVE_NO_PROMPT;
|
|
gScanProgressIndicator = 0;
|
|
}
|
|
|
|
void SCANNER_Stop(void)
|
|
{
|
|
if(SCANNER_IsScanning()) {
|
|
gEeprom.CROSS_BAND_RX_TX = gBackup_CROSS_BAND_RX_TX;
|
|
gVfoConfigureMode = VFO_CONFIGURE_RELOAD;
|
|
gFlagResetVfos = true;
|
|
gUpdateStatus = true;
|
|
gCssBackgroundScan = false;
|
|
gScanUseCssResult = false;
|
|
#ifdef ENABLE_VOICE
|
|
gAnotherVoiceID = VOICE_ID_CANCEL;
|
|
#endif
|
|
BK4819_StopScan();
|
|
}
|
|
}
|
|
|
|
void SCANNER_TimeSlice10ms(void)
|
|
{
|
|
if (!SCANNER_IsScanning())
|
|
return;
|
|
|
|
if (gScanDelay_10ms > 0) {
|
|
gScanDelay_10ms--;
|
|
return;
|
|
}
|
|
|
|
if (gScannerSaveState != SCAN_SAVE_NO_PROMPT) {
|
|
return;
|
|
}
|
|
|
|
switch (gScanCssState) {
|
|
case SCAN_CSS_STATE_OFF: {
|
|
// must be RF frequency scanning if we're here ?
|
|
uint32_t result;
|
|
if (!BK4819_GetFrequencyScanResult(&result))
|
|
break;
|
|
|
|
int32_t delta = result - gScanFrequency;
|
|
gScanFrequency = result;
|
|
|
|
if (delta < 0)
|
|
delta = -delta;
|
|
if (delta < 100)
|
|
scanHitCount++;
|
|
else
|
|
scanHitCount = 0;
|
|
|
|
BK4819_DisableFrequencyScan();
|
|
|
|
if (scanHitCount < 3) {
|
|
BK4819_EnableFrequencyScan();
|
|
}
|
|
else {
|
|
BK4819_SetScanFrequency(gScanFrequency);
|
|
gScanCssResultCode = 0xFF;
|
|
gScanCssResultType = 0xFF;
|
|
scanHitCount = 0;
|
|
gScanUseCssResult = false;
|
|
gScanProgressIndicator = 0;
|
|
gScanCssState = SCAN_CSS_STATE_SCANNING;
|
|
|
|
if(!gCssBackgroundScan)
|
|
GUI_SelectNextDisplay(DISPLAY_SCANNER);
|
|
|
|
gUpdateStatus = true;
|
|
}
|
|
|
|
gScanDelay_10ms = scan_delay_10ms;
|
|
//gScanDelay_10ms = 1; // 10ms
|
|
break;
|
|
}
|
|
case SCAN_CSS_STATE_SCANNING: {
|
|
uint32_t cdcssFreq;
|
|
uint16_t ctcssFreq;
|
|
BK4819_CssScanResult_t scanResult = BK4819_GetCxCSSScanResult(&cdcssFreq, &ctcssFreq);
|
|
if (scanResult == BK4819_CSS_RESULT_NOT_FOUND)
|
|
break;
|
|
|
|
BK4819_Disable();
|
|
|
|
if (scanResult == BK4819_CSS_RESULT_CDCSS) {
|
|
const uint8_t Code = DCS_GetCdcssCode(cdcssFreq);
|
|
if (Code != 0xFF)
|
|
{
|
|
gScanCssResultCode = Code;
|
|
gScanCssResultType = CODE_TYPE_DIGITAL;
|
|
gScanCssState = SCAN_CSS_STATE_FOUND;
|
|
gScanUseCssResult = true;
|
|
gUpdateStatus = true;
|
|
}
|
|
}
|
|
else if (scanResult == BK4819_CSS_RESULT_CTCSS) {
|
|
const uint8_t Code = DCS_GetCtcssCode(ctcssFreq);
|
|
if (Code != 0xFF) {
|
|
if (Code == gScanCssResultCode && gScanCssResultType == CODE_TYPE_CONTINUOUS_TONE) {
|
|
if (++scanHitCount >= 2) {
|
|
gScanCssState = SCAN_CSS_STATE_FOUND;
|
|
gScanUseCssResult = true;
|
|
gUpdateStatus = true;
|
|
}
|
|
}
|
|
else
|
|
scanHitCount = 0;
|
|
|
|
gScanCssResultType = CODE_TYPE_CONTINUOUS_TONE;
|
|
gScanCssResultCode = Code;
|
|
}
|
|
}
|
|
|
|
if (gScanCssState < SCAN_CSS_STATE_FOUND) { // scanning or off
|
|
BK4819_SetScanFrequency(gScanFrequency);
|
|
gScanDelay_10ms = scan_delay_10ms;
|
|
break;
|
|
}
|
|
|
|
if(gCssBackgroundScan) {
|
|
gCssBackgroundScan = false;
|
|
if(gScanUseCssResult)
|
|
MENU_CssScanFound();
|
|
}
|
|
else
|
|
GUI_SelectNextDisplay(DISPLAY_SCANNER);
|
|
|
|
|
|
break;
|
|
}
|
|
default:
|
|
gCssBackgroundScan = false;
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
void SCANNER_TimeSlice500ms(void)
|
|
{
|
|
if (SCANNER_IsScanning() && gScannerSaveState == SCAN_SAVE_NO_PROMPT && gScanCssState < SCAN_CSS_STATE_FOUND) {
|
|
gScanProgressIndicator++;
|
|
#ifdef ENABLE_NO_CODE_SCAN_TIMEOUT
|
|
if (gScanProgressIndicator > 32) {
|
|
if (gScanCssState == SCAN_CSS_STATE_SCANNING && !gScanSingleFrequency)
|
|
gScanCssState = SCAN_CSS_STATE_FOUND;
|
|
else
|
|
gScanCssState = SCAN_CSS_STATE_FAILED;
|
|
|
|
gUpdateStatus = true;
|
|
}
|
|
#endif
|
|
gUpdateDisplay = true;
|
|
}
|
|
else if(gCssBackgroundScan) {
|
|
gUpdateDisplay = true;
|
|
}
|
|
}
|
|
|
|
bool SCANNER_IsScanning(void)
|
|
{
|
|
return gCssBackgroundScan || (gScreenToDisplay == DISPLAY_SCANNER);
|
|
} |