mirror of
https://github.com/OneOfEleven/uv-k5-firmware-custom.git
synced 2025-04-27 22:01:26 +03:00
1363 lines
30 KiB
C
1363 lines
30 KiB
C
/* Copyright 2023 fagci
|
|
* https://github.com/fagci
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "../app/spectrum.h"
|
|
|
|
#define F_MIN FrequencyBandTable[0].lower
|
|
#define F_MAX FrequencyBandTable[ARRAY_SIZE(FrequencyBandTable) - 1].upper
|
|
|
|
const uint16_t RSSI_MAX_VALUE = 65535;
|
|
|
|
static uint32_t initialFreq;
|
|
static char String[32];
|
|
|
|
bool isInitialized = false;
|
|
bool monitorMode = false;
|
|
bool redrawStatus = true;
|
|
bool redrawScreen = false;
|
|
bool newScanStart = true;
|
|
bool preventKeypress = true;
|
|
|
|
bool isListening = false;
|
|
bool isTransmitting = false;
|
|
|
|
State currentState = SPECTRUM, previousState = SPECTRUM;
|
|
|
|
PeakInfo peak;
|
|
ScanInfo scanInfo;
|
|
KeyboardState kbd = {KEY_INVALID, KEY_INVALID, 0};
|
|
|
|
const char *bwOptions[] = {" 25k", "12.5k", "6.25k"};
|
|
const char *modulationTypeOptions[] = {" FM", " AM", "USB"};
|
|
const uint8_t modulationTypeTuneSteps[] = {100, 50, 10};
|
|
const uint8_t modTypeReg47Values[] = {1, 7, 5};
|
|
|
|
SpectrumSettings settings = {
|
|
.stepsCount = STEPS_64,
|
|
.scanStepIndex = S_STEP_25_0kHz,
|
|
.frequencyChangeStep = 80000,
|
|
.scanDelay = 3200,
|
|
.rssiTriggerLevel = 150,
|
|
.backlightState = true,
|
|
.bw = BK4819_FILTER_BW_WIDE,
|
|
.listenBw = BK4819_FILTER_BW_WIDE,
|
|
.modulationType = false,
|
|
};
|
|
|
|
uint32_t fMeasure = 0;
|
|
uint32_t fTx = 0;
|
|
uint32_t currentFreq;
|
|
uint32_t tempFreq;
|
|
uint16_t rssiHistory[128] = {0};
|
|
bool blacklist[128] = {false};
|
|
|
|
static const RegisterSpec afOutRegSpec = {"AF OUT", 0x47, 8, 0xF, 1};
|
|
static const RegisterSpec afDacGainRegSpec = {"AF DAC G", 0x48, 0, 0xF, 1};
|
|
static const RegisterSpec registerSpecs[] = {
|
|
{},
|
|
{"LNAs", 0x13, 8, 0b11, 1},
|
|
{"LNA", 0x13, 5, 0b111, 1},
|
|
{"PGA", 0x13, 0, 0b111, 1},
|
|
{"MIX", 0x13, 3, 0b11, 1},
|
|
{"DEV", 0x40, 0, 4095, 1},
|
|
{"CMP", 0x31, 3, 1, 1},
|
|
{"MIC", 0x7D, 0, 0x1F, 1},
|
|
};
|
|
|
|
static uint16_t registersBackup[128];
|
|
static const uint8_t registersToBackup[] = {
|
|
0x13, 0x30, 0x31, 0x37, 0x3D, 0x40, 0x43, 0x47, 0x48, 0x7D, 0x7E,
|
|
};
|
|
|
|
static MovingAverage mov = {{128}, {}, 255, 128, 0, 0};
|
|
static const uint8_t MOV_N = ARRAY_SIZE(mov.buf);
|
|
|
|
const uint8_t FREQ_INPUT_LENGTH = 10;
|
|
uint8_t freqInputIndex = 0;
|
|
uint8_t freqInputDotIndex = 0;
|
|
key_code_t freqInputArr[10];
|
|
char freqInputString[] = "----------"; // XXXX.XXXXX
|
|
|
|
uint8_t menuState = 0;
|
|
|
|
uint16_t listenT = 0;
|
|
|
|
uint16_t batteryUpdateTimer = 0;
|
|
bool isMovingInitialized = false;
|
|
uint8_t lastStepsCount = 0;
|
|
|
|
uint8_t CountBits(uint16_t n)
|
|
{
|
|
uint8_t count = 0;
|
|
while (n)
|
|
{
|
|
count++;
|
|
n >>= 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static uint16_t GetRegMask(RegisterSpec s)
|
|
{
|
|
return (1 << CountBits(s.maxValue)) - 1;
|
|
}
|
|
|
|
static uint16_t GetRegValue(RegisterSpec s)
|
|
{
|
|
return (BK4819_ReadRegister(s.num) >> s.offset) & s.maxValue;
|
|
}
|
|
|
|
static void SetRegValue(RegisterSpec s, uint16_t v)
|
|
{
|
|
uint16_t reg = BK4819_ReadRegister(s.num);
|
|
reg &= ~(GetRegMask(s) << s.offset);
|
|
BK4819_WriteRegister(s.num, reg | (v << s.offset));
|
|
}
|
|
|
|
static void UpdateRegMenuValue(RegisterSpec s, bool add)
|
|
{
|
|
uint16_t v = GetRegValue(s);
|
|
|
|
if (add && v <= s.maxValue - s.inc)
|
|
v += s.inc;
|
|
else
|
|
if (!add && v >= 0 + s.inc)
|
|
v -= s.inc;
|
|
|
|
SetRegValue(s, v);
|
|
redrawScreen = true;
|
|
}
|
|
|
|
// Utility functions
|
|
|
|
key_code_t GetKey()
|
|
{
|
|
key_code_t btn = KEYBOARD_Poll();
|
|
if (btn == KEY_INVALID && !GPIO_CheckBit(&GPIOC->DATA, GPIOC_PIN_PTT))
|
|
btn = KEY_PTT;
|
|
return btn;
|
|
}
|
|
|
|
void SetState(State state)
|
|
{
|
|
previousState = currentState;
|
|
currentState = state;
|
|
redrawScreen = true;
|
|
redrawStatus = true;
|
|
}
|
|
|
|
// Radio functions
|
|
|
|
static void BackupRegisters()
|
|
{
|
|
for (int i = 0; i < ARRAY_SIZE(registersToBackup); ++i)
|
|
{
|
|
uint8_t regNum = registersToBackup[i];
|
|
registersBackup[regNum] = BK4819_ReadRegister(regNum);
|
|
}
|
|
}
|
|
|
|
static void RestoreRegisters()
|
|
{
|
|
for (int i = 0; i < ARRAY_SIZE(registersToBackup); ++i)
|
|
{
|
|
uint8_t regNum = registersToBackup[i];
|
|
BK4819_WriteRegister(regNum, registersBackup[regNum]);
|
|
}
|
|
}
|
|
|
|
static void SetModulation(ModulationType type)
|
|
{
|
|
// restore only registers, which we affect here fully
|
|
BK4819_WriteRegister(0x37, registersBackup[0x37]);
|
|
BK4819_WriteRegister(0x3D, registersBackup[0x3D]);
|
|
BK4819_WriteRegister(0x48, registersBackup[0x48]);
|
|
|
|
SetRegValue(afOutRegSpec, modTypeReg47Values[type]);
|
|
|
|
if (type == MOD_USB)
|
|
{
|
|
BK4819_WriteRegister(0x37, 0b0001011000001111);
|
|
BK4819_WriteRegister(0x3D, 0b0010101101000101);
|
|
BK4819_WriteRegister(0x48, 0b0000001110101000);
|
|
}
|
|
|
|
if (type == MOD_AM)
|
|
SetRegValue(afDacGainRegSpec, 0xE);
|
|
}
|
|
|
|
static void SetF(uint32_t f) {
|
|
fMeasure = f;
|
|
|
|
BK4819_set_rf_frequency(fMeasure);
|
|
BK4819_set_rf_filter_path(fMeasure);
|
|
uint16_t reg = BK4819_ReadRegister(BK4819_REG_30);
|
|
BK4819_WriteRegister(BK4819_REG_30, 0);
|
|
BK4819_WriteRegister(BK4819_REG_30, reg);
|
|
}
|
|
|
|
static void SetTxF(uint32_t f) {
|
|
fTx = f;
|
|
BK4819_set_rf_frequency(f);
|
|
BK4819_set_rf_filter_path(f);
|
|
uint16_t reg = BK4819_ReadRegister(BK4819_REG_30);
|
|
BK4819_WriteRegister(BK4819_REG_30, 0);
|
|
BK4819_WriteRegister(BK4819_REG_30, reg);
|
|
}
|
|
|
|
// Spectrum related
|
|
|
|
bool IsPeakOverLevel() { return peak.rssi >= settings.rssiTriggerLevel; }
|
|
|
|
static void ResetPeak() {
|
|
peak.t = 0;
|
|
peak.rssi = 0;
|
|
}
|
|
|
|
bool IsCenterMode() { return settings.scanStepIndex < S_STEP_1_0kHz; }
|
|
uint8_t GetStepsCount() { return 128 >> settings.stepsCount; }
|
|
uint16_t GetScanStep() { return scanStepValues[settings.scanStepIndex]; }
|
|
uint32_t GetBW() { return GetStepsCount() * GetScanStep(); }
|
|
uint32_t GetFStart() {
|
|
return IsCenterMode() ? currentFreq - (GetBW() >> 1) : currentFreq;
|
|
}
|
|
uint32_t GetFEnd() { return currentFreq + GetBW(); }
|
|
|
|
static void MovingCp(uint16_t *dst, uint16_t *src) {
|
|
memcpy(dst, src, GetStepsCount() * sizeof(uint16_t));
|
|
}
|
|
|
|
static void ResetMoving() {
|
|
for (int i = 0; i < MOV_N; ++i) {
|
|
MovingCp(mov.buf[i], rssiHistory);
|
|
}
|
|
}
|
|
|
|
static void MoveHistory() {
|
|
const uint8_t XN = GetStepsCount();
|
|
|
|
uint32_t midSum = 0;
|
|
|
|
mov.min = RSSI_MAX_VALUE;
|
|
mov.max = 0;
|
|
|
|
if (lastStepsCount != XN) {
|
|
ResetMoving();
|
|
lastStepsCount = XN;
|
|
}
|
|
for (int i = MOV_N - 1; i > 0; --i) {
|
|
MovingCp(mov.buf[i], mov.buf[i - 1]);
|
|
}
|
|
MovingCp(mov.buf[0], rssiHistory);
|
|
|
|
uint8_t skipped = 0;
|
|
|
|
for (int x = 0; x < XN; ++x) {
|
|
if (blacklist[x]) {
|
|
skipped++;
|
|
continue;
|
|
}
|
|
uint32_t sum = 0;
|
|
for (int i = 0; i < MOV_N; ++i) {
|
|
sum += mov.buf[i][x];
|
|
}
|
|
|
|
uint16_t pointV = mov.mean[x] = sum / MOV_N;
|
|
|
|
midSum += pointV;
|
|
|
|
if (pointV > mov.max) {
|
|
mov.max = pointV;
|
|
}
|
|
|
|
if (pointV < mov.min) {
|
|
mov.min = pointV;
|
|
}
|
|
}
|
|
if (skipped == XN) {
|
|
return;
|
|
}
|
|
|
|
mov.mid = midSum / (XN - skipped);
|
|
}
|
|
|
|
static void TuneToPeak() {
|
|
scanInfo.f = peak.f;
|
|
scanInfo.rssi = peak.rssi;
|
|
scanInfo.i = peak.i;
|
|
SetF(scanInfo.f);
|
|
}
|
|
|
|
uint8_t GetBWRegValueForScan() {
|
|
return scanStepBWRegValues[settings.scanStepIndex == S_STEP_100_0kHz ? 11
|
|
: 0];
|
|
}
|
|
|
|
uint8_t GetBWRegValueForListen() {
|
|
return listenBWRegValues[settings.listenBw];
|
|
}
|
|
|
|
static void ResetRSSI() {
|
|
uint32_t Reg = BK4819_ReadRegister(BK4819_REG_30);
|
|
Reg &= ~1;
|
|
BK4819_WriteRegister(BK4819_REG_30, Reg);
|
|
Reg |= 1;
|
|
BK4819_WriteRegister(BK4819_REG_30, Reg);
|
|
}
|
|
|
|
uint16_t GetRssi() {
|
|
if (currentState == SPECTRUM) {
|
|
ResetRSSI();
|
|
SYSTICK_DelayUs(3200);
|
|
}
|
|
return BK4819_GetRSSI();
|
|
}
|
|
|
|
uint32_t GetOffsetedF(uint32_t f) {
|
|
switch (gCurrentVfo->FREQUENCY_DEVIATION_SETTING) {
|
|
case FREQUENCY_DEVIATION_OFF:
|
|
break;
|
|
case FREQUENCY_DEVIATION_ADD:
|
|
f += gCurrentVfo->FREQUENCY_OF_DEVIATION;
|
|
break;
|
|
case FREQUENCY_DEVIATION_SUB:
|
|
f -= gCurrentVfo->FREQUENCY_OF_DEVIATION;
|
|
break;
|
|
}
|
|
|
|
return Clamp(f, FrequencyBandTable[0].lower,
|
|
FrequencyBandTable[ARRAY_SIZE(FrequencyBandTable) - 1].upper);
|
|
}
|
|
|
|
bool IsTXAllowed() { return g_setting_ALL_TX != 2; }
|
|
|
|
static void ToggleAudio(bool on) {
|
|
if (on)
|
|
GPIO_SetBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER);
|
|
else {
|
|
GPIO_ClearBit(&GPIOC->DATA, GPIOC_PIN_SPEAKER);
|
|
}
|
|
}
|
|
|
|
static void ToggleTX(bool);
|
|
static void ToggleRX(bool);
|
|
|
|
static void ToggleRX(bool on) {
|
|
/* if (isListening == on) {
|
|
return;
|
|
} */
|
|
isListening = on;
|
|
if (on) {
|
|
ToggleTX(false);
|
|
}
|
|
|
|
BK4819_set_GPIO_pin(BK4819_GPIO6_PIN2_GREEN, on);
|
|
BK4819_RX_TurnOn();
|
|
|
|
ToggleAudio(on);
|
|
BK4819_ToggleAFDAC(on);
|
|
BK4819_ToggleAFBit(on);
|
|
|
|
if (on) {
|
|
listenT = 1000;
|
|
BK4819_WriteRegister(0x43, GetBWRegValueForListen());
|
|
} else {
|
|
BK4819_WriteRegister(0x43, GetBWRegValueForScan());
|
|
}
|
|
}
|
|
|
|
uint16_t registersVault[128] = {0};
|
|
|
|
static void RegBackupSet(uint8_t num, uint16_t value) {
|
|
registersVault[num] = BK4819_ReadRegister(num);
|
|
BK4819_WriteRegister(num, value);
|
|
}
|
|
|
|
static void RegRestore(uint8_t num) {
|
|
BK4819_WriteRegister(num, registersVault[num]);
|
|
}
|
|
|
|
static void ToggleTX(bool on) {
|
|
if (isTransmitting == on) {
|
|
return;
|
|
}
|
|
isTransmitting = on;
|
|
if (on) {
|
|
ToggleRX(false);
|
|
}
|
|
|
|
BK4819_set_GPIO_pin(BK4819_GPIO5_PIN1_RED, on);
|
|
|
|
if (on) {
|
|
ToggleAudio(false);
|
|
|
|
SetTxF(GetOffsetedF(fMeasure));
|
|
|
|
RegBackupSet(BK4819_REG_47, 0x6040);
|
|
RegBackupSet(BK4819_REG_7E, 0x302E);
|
|
RegBackupSet(BK4819_REG_50, 0x3B20);
|
|
RegBackupSet(BK4819_REG_37, 0x1D0F);
|
|
RegBackupSet(BK4819_REG_52, 0x028F);
|
|
RegBackupSet(BK4819_REG_30, 0x0000);
|
|
BK4819_WriteRegister(BK4819_REG_30, 0xC1FE);
|
|
RegBackupSet(BK4819_REG_51, 0x0000);
|
|
|
|
BK4819_SetupPowerAmplifier(gCurrentVfo->TXP_CalculatedSetting,
|
|
gCurrentVfo->p_tx->Frequency);
|
|
}
|
|
else
|
|
{
|
|
RADIO_tx_eot();
|
|
RADIO_EnableCxCSS();
|
|
|
|
BK4819_SetupPowerAmplifier(0, 0);
|
|
|
|
RegRestore(BK4819_REG_51);
|
|
BK4819_WriteRegister(BK4819_REG_30, 0);
|
|
RegRestore(BK4819_REG_30);
|
|
RegRestore(BK4819_REG_52);
|
|
RegRestore(BK4819_REG_37);
|
|
RegRestore(BK4819_REG_50);
|
|
RegRestore(BK4819_REG_7E);
|
|
RegRestore(BK4819_REG_47);
|
|
|
|
SetF(fMeasure);
|
|
}
|
|
|
|
BK4819_set_GPIO_pin(BK4819_GPIO0_PIN28_RX_ENABLE, !on);
|
|
BK4819_set_GPIO_pin(BK4819_GPIO1_PIN29_PA_ENABLE, on);
|
|
}
|
|
|
|
// Scan info
|
|
|
|
static void ResetScanStats() {
|
|
scanInfo.rssi = 0;
|
|
scanInfo.rssiMax = 0;
|
|
scanInfo.iPeak = 0;
|
|
scanInfo.fPeak = 0;
|
|
}
|
|
|
|
static void InitScan() {
|
|
ResetScanStats();
|
|
scanInfo.i = 0;
|
|
scanInfo.f = GetFStart();
|
|
|
|
scanInfo.scanStep = GetScanStep();
|
|
scanInfo.measurementsCount = GetStepsCount();
|
|
}
|
|
|
|
static void ResetBlacklist() {
|
|
for (int i = 0; i < 128; ++i) {
|
|
if (blacklist[i])
|
|
blacklist[i] = false;
|
|
}
|
|
}
|
|
|
|
static void RelaunchScan() {
|
|
InitScan();
|
|
ResetPeak();
|
|
lastStepsCount = 0;
|
|
ToggleRX(false);
|
|
#ifdef SPECTRUM_AUTOMATIC_SQUELCH
|
|
settings.rssiTriggerLevel = RSSI_MAX_VALUE;
|
|
#endif
|
|
preventKeypress = true;
|
|
scanInfo.rssiMin = RSSI_MAX_VALUE;
|
|
}
|
|
|
|
static void UpdateScanInfo() {
|
|
if (scanInfo.rssi > scanInfo.rssiMax) {
|
|
scanInfo.rssiMax = scanInfo.rssi;
|
|
scanInfo.fPeak = scanInfo.f;
|
|
scanInfo.iPeak = scanInfo.i;
|
|
}
|
|
|
|
if (scanInfo.rssi < scanInfo.rssiMin) {
|
|
scanInfo.rssiMin = scanInfo.rssi;
|
|
}
|
|
}
|
|
|
|
static void AutoTriggerLevel() {
|
|
if (settings.rssiTriggerLevel == RSSI_MAX_VALUE) {
|
|
settings.rssiTriggerLevel = Clamp(scanInfo.rssiMax + 4, 0, RSSI_MAX_VALUE);
|
|
}
|
|
}
|
|
|
|
static void UpdatePeakInfoForce() {
|
|
peak.t = 0;
|
|
peak.rssi = scanInfo.rssiMax;
|
|
peak.f = scanInfo.fPeak;
|
|
peak.i = scanInfo.iPeak;
|
|
AutoTriggerLevel();
|
|
}
|
|
|
|
static void UpdatePeakInfo() {
|
|
if (peak.f == 0 || peak.t >= 1024 || peak.rssi < scanInfo.rssiMax)
|
|
UpdatePeakInfoForce();
|
|
}
|
|
|
|
static void Measure() { rssiHistory[scanInfo.i] = scanInfo.rssi = GetRssi(); }
|
|
|
|
// Update things by keypress
|
|
|
|
static void UpdateRssiTriggerLevel(bool inc) {
|
|
if (inc)
|
|
settings.rssiTriggerLevel += 2;
|
|
else
|
|
settings.rssiTriggerLevel -= 2;
|
|
redrawScreen = true;
|
|
SYSTEM_DelayMs(10);
|
|
}
|
|
|
|
static void ApplyPreset(FreqPreset p) {
|
|
currentFreq = p.fStart;
|
|
settings.scanStepIndex = p.stepSizeIndex;
|
|
settings.listenBw = p.listenBW;
|
|
settings.modulationType = p.modulationType;
|
|
settings.stepsCount = p.stepsCountIndex;
|
|
SetModulation(settings.modulationType);
|
|
RelaunchScan();
|
|
ResetBlacklist();
|
|
redrawScreen = true;
|
|
}
|
|
|
|
static void SelectNearestPreset(bool inc) {
|
|
FreqPreset p;
|
|
const uint8_t SZ = ARRAY_SIZE(freqPresets);
|
|
if (inc) {
|
|
for (int i = 0; i < SZ; ++i) {
|
|
p = freqPresets[i];
|
|
if (currentFreq < p.fStart) {
|
|
ApplyPreset(p);
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
for (int i = SZ - 1; i >= 0; --i) {
|
|
p = freqPresets[i];
|
|
if (currentFreq > p.fEnd) {
|
|
ApplyPreset(p);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
ApplyPreset(p);
|
|
}
|
|
|
|
static void UpdateScanStep(bool inc) {
|
|
if (inc && settings.scanStepIndex < S_STEP_100_0kHz) {
|
|
settings.scanStepIndex++;
|
|
} else if (!inc && settings.scanStepIndex > 0) {
|
|
settings.scanStepIndex--;
|
|
} else {
|
|
return;
|
|
}
|
|
settings.frequencyChangeStep = GetBW() >> 1;
|
|
RelaunchScan();
|
|
ResetBlacklist();
|
|
redrawScreen = true;
|
|
}
|
|
|
|
static void UpdateCurrentFreq(bool inc) {
|
|
if (inc && currentFreq < F_MAX) {
|
|
currentFreq += settings.frequencyChangeStep;
|
|
} else if (!inc && currentFreq > F_MIN) {
|
|
currentFreq -= settings.frequencyChangeStep;
|
|
} else {
|
|
return;
|
|
}
|
|
RelaunchScan();
|
|
ResetBlacklist();
|
|
redrawScreen = true;
|
|
}
|
|
|
|
static void UpdateCurrentFreqStill(bool inc) {
|
|
uint8_t offset = modulationTypeTuneSteps[settings.modulationType];
|
|
uint32_t f = fMeasure;
|
|
if (inc && f < F_MAX) {
|
|
f += offset;
|
|
} else if (!inc && f > F_MIN) {
|
|
f -= offset;
|
|
}
|
|
SetF(f);
|
|
redrawScreen = true;
|
|
}
|
|
|
|
static void UpdateFreqChangeStep(bool inc) {
|
|
uint16_t diff = GetScanStep() * 4;
|
|
if (inc && settings.frequencyChangeStep < 200000) {
|
|
settings.frequencyChangeStep += diff;
|
|
} else if (!inc && settings.frequencyChangeStep > 10000) {
|
|
settings.frequencyChangeStep -= diff;
|
|
}
|
|
SYSTEM_DelayMs(100);
|
|
redrawScreen = true;
|
|
}
|
|
|
|
static void ToggleModulation() {
|
|
if (settings.modulationType < MOD_USB) {
|
|
settings.modulationType++;
|
|
} else {
|
|
settings.modulationType = MOD_FM;
|
|
}
|
|
SetModulation(settings.modulationType);
|
|
redrawScreen = true;
|
|
}
|
|
|
|
static void ToggleListeningBW() {
|
|
if (settings.listenBw == BK4819_FILTER_BW_NARROWER) {
|
|
settings.listenBw = BK4819_FILTER_BW_WIDE;
|
|
} else {
|
|
settings.listenBw++;
|
|
}
|
|
redrawScreen = true;
|
|
}
|
|
|
|
static void ToggleBacklight() {
|
|
settings.backlightState = !settings.backlightState;
|
|
if (settings.backlightState) {
|
|
GPIO_SetBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT);
|
|
} else {
|
|
GPIO_ClearBit(&GPIOB->DATA, GPIOB_PIN_BACKLIGHT);
|
|
}
|
|
}
|
|
|
|
static void ToggleStepsCount() {
|
|
if (settings.stepsCount == STEPS_128) {
|
|
settings.stepsCount = STEPS_16;
|
|
} else {
|
|
settings.stepsCount--;
|
|
}
|
|
settings.frequencyChangeStep = GetBW() >> 1;
|
|
RelaunchScan();
|
|
ResetBlacklist();
|
|
redrawScreen = true;
|
|
}
|
|
|
|
static void ResetFreqInput() {
|
|
tempFreq = 0;
|
|
for (int i = 0; i < 10; ++i) {
|
|
freqInputString[i] = '-';
|
|
}
|
|
}
|
|
|
|
static void FreqInput() {
|
|
freqInputIndex = 0;
|
|
freqInputDotIndex = 0;
|
|
ResetFreqInput();
|
|
SetState(FREQ_INPUT);
|
|
}
|
|
|
|
static void UpdateFreqInput(key_code_t key) {
|
|
if (key != KEY_EXIT && freqInputIndex >= 10) {
|
|
return;
|
|
}
|
|
if (key == KEY_STAR) {
|
|
if (freqInputIndex == 0 || freqInputDotIndex) {
|
|
return;
|
|
}
|
|
freqInputDotIndex = freqInputIndex;
|
|
}
|
|
if (key == KEY_EXIT) {
|
|
freqInputIndex--;
|
|
} else {
|
|
freqInputArr[freqInputIndex++] = key;
|
|
}
|
|
|
|
ResetFreqInput();
|
|
|
|
uint8_t dotIndex =
|
|
freqInputDotIndex == 0 ? freqInputIndex : freqInputDotIndex;
|
|
|
|
key_code_t digitKey;
|
|
for (int i = 0; i < 10; ++i) {
|
|
if (i < freqInputIndex) {
|
|
digitKey = freqInputArr[i];
|
|
freqInputString[i] = digitKey <= KEY_9 ? '0' + digitKey : '.';
|
|
} else {
|
|
freqInputString[i] = '-';
|
|
}
|
|
}
|
|
|
|
uint32_t base = 100000; // 1MHz in BK units
|
|
for (int i = dotIndex - 1; i >= 0; --i) {
|
|
tempFreq += freqInputArr[i] * base;
|
|
base *= 10;
|
|
}
|
|
|
|
base = 10000; // 0.1MHz in BK units
|
|
if (dotIndex < freqInputIndex) {
|
|
for (int i = dotIndex + 1; i < freqInputIndex; ++i) {
|
|
tempFreq += freqInputArr[i] * base;
|
|
base /= 10;
|
|
}
|
|
}
|
|
redrawScreen = true;
|
|
}
|
|
|
|
static void Blacklist() {
|
|
blacklist[peak.i] = true;
|
|
ResetPeak();
|
|
ToggleRX(false);
|
|
newScanStart = true;
|
|
}
|
|
|
|
// Draw things
|
|
|
|
static uint8_t Rssi2Y(uint16_t rssi) {
|
|
return DrawingEndY - ConvertDomain(rssi, mov.min - 2,
|
|
mov.max + 30 + (mov.max - mov.min) / 3, 0,
|
|
DrawingEndY);
|
|
}
|
|
|
|
static void DrawSpectrum() {
|
|
for (uint8_t x = 0; x < 128; ++x) {
|
|
uint8_t i = x >> settings.stepsCount;
|
|
if (blacklist[i]) {
|
|
continue;
|
|
}
|
|
uint16_t rssi = rssiHistory[i];
|
|
DrawHLine(Rssi2Y(rssi), DrawingEndY, x, true);
|
|
}
|
|
}
|
|
|
|
static void UpdateBatteryInfo() {
|
|
for (int i = 0; i < 4; i++) {
|
|
BOARD_ADC_GetBatteryInfo(&g_battery_voltages[i], &g_usb_current);
|
|
}
|
|
|
|
uint16_t voltage = Mid(g_battery_voltages, ARRAY_SIZE(g_battery_voltages));
|
|
g_battery_display_level = 0;
|
|
|
|
for (int i = ARRAY_SIZE(g_battery_calibration) - 1; i >= 0; --i) {
|
|
if (g_battery_calibration[i] < voltage) {
|
|
g_battery_display_level = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void DrawStatus() {
|
|
|
|
g_status_line[127] = 0b01111110;
|
|
for (int i = 126; i >= 116; i--) {
|
|
g_status_line[i] = 0b01000010;
|
|
}
|
|
uint8_t v = g_battery_display_level;
|
|
v <<= 1;
|
|
for (int i = 125; i >= 116; i--) {
|
|
if (126 - i <= v) {
|
|
g_status_line[i + 2] = 0b01111110;
|
|
}
|
|
}
|
|
g_status_line[117] = 0b01111110;
|
|
g_status_line[116] = 0b00011000;
|
|
}
|
|
|
|
static void DrawF(uint32_t f) {
|
|
sprintf(String, "%u.%05u", f / 100000, f % 100000);
|
|
|
|
if (currentState == STILL && kbd.current == KEY_PTT) {
|
|
if (g_battery_display_level == 6) {
|
|
sprintf(String, "VOLTAGE HIGH");
|
|
} else if (!IsTXAllowed()) {
|
|
sprintf(String, "DISABLED");
|
|
} else {
|
|
f = GetOffsetedF(f);
|
|
sprintf(String, "TX %u.%05u", f / 100000, f % 100000);
|
|
}
|
|
}
|
|
UI_PrintStringSmall(String, 8, 127, 0);
|
|
|
|
sprintf(String, "%s", modulationTypeOptions[settings.modulationType]);
|
|
UI_PrintStringSmallest(String, 116, 1, false, true);
|
|
sprintf(String, "%s", bwOptions[settings.listenBw]);
|
|
UI_PrintStringSmallest(String, 108, 7, false, true);
|
|
}
|
|
|
|
static void DrawNums() {
|
|
if (currentState == SPECTRUM) {
|
|
sprintf(String, "%ux", GetStepsCount());
|
|
UI_PrintStringSmallest(String, 0, 1, false, true);
|
|
sprintf(String, "%u.%02uk", GetScanStep() / 100, GetScanStep() % 100);
|
|
UI_PrintStringSmallest(String, 0, 7, false, true);
|
|
}
|
|
|
|
if (IsCenterMode()) {
|
|
sprintf(String, "%u.%05u \xB1%u.%02uk", currentFreq / 100000,
|
|
currentFreq % 100000, settings.frequencyChangeStep / 100,
|
|
settings.frequencyChangeStep % 100);
|
|
UI_PrintStringSmallest(String, 36, 49, false, true);
|
|
} else {
|
|
sprintf(String, "%u.%05u", GetFStart() / 100000, GetFStart() % 100000);
|
|
UI_PrintStringSmallest(String, 0, 49, false, true);
|
|
|
|
sprintf(String, "\xB1%u.%02uk", settings.frequencyChangeStep / 100,
|
|
settings.frequencyChangeStep % 100);
|
|
UI_PrintStringSmallest(String, 48, 49, false, true);
|
|
|
|
sprintf(String, "%u.%05u", GetFEnd() / 100000, GetFEnd() % 100000);
|
|
UI_PrintStringSmallest(String, 93, 49, false, true);
|
|
}
|
|
}
|
|
|
|
static void DrawRssiTriggerLevel() {
|
|
if (settings.rssiTriggerLevel == RSSI_MAX_VALUE || monitorMode)
|
|
return;
|
|
uint8_t y = Rssi2Y(settings.rssiTriggerLevel);
|
|
for (uint8_t x = 0; x < 128; x += 2) {
|
|
PutPixel(x, y, true);
|
|
}
|
|
}
|
|
|
|
static void DrawTicks() {
|
|
uint32_t f = GetFStart() % 100000;
|
|
uint32_t step = GetScanStep();
|
|
for (uint8_t i = 0; i < 128; i += (1 << settings.stepsCount), f += step) {
|
|
uint8_t barValue = 0b00000001;
|
|
(f % 10000) < step && (barValue |= 0b00000010);
|
|
(f % 50000) < step && (barValue |= 0b00000100);
|
|
(f % 100000) < step && (barValue |= 0b00011000);
|
|
|
|
g_frame_buffer[5][i] |= barValue;
|
|
}
|
|
|
|
// center
|
|
/* if (IsCenterMode()) {
|
|
g_frame_buffer[5][62] = 0x80;
|
|
g_frame_buffer[5][63] = 0x80;
|
|
g_frame_buffer[5][64] = 0xff;
|
|
g_frame_buffer[5][65] = 0x80;
|
|
g_frame_buffer[5][66] = 0x80;
|
|
} else {
|
|
g_frame_buffer[5][0] = 0xff;
|
|
g_frame_buffer[5][1] = 0x80;
|
|
g_frame_buffer[5][2] = 0x80;
|
|
g_frame_buffer[5][3] = 0x80;
|
|
g_frame_buffer[5][124] = 0x80;
|
|
g_frame_buffer[5][125] = 0x80;
|
|
g_frame_buffer[5][126] = 0x80;
|
|
g_frame_buffer[5][127] = 0xff;
|
|
} */
|
|
}
|
|
|
|
static void DrawArrow(uint8_t x) {
|
|
for (signed i = -2; i <= 2; ++i) {
|
|
signed v = x + i;
|
|
uint8_t a = i > 0 ? i : -i;
|
|
if (!(v & 128)) {
|
|
g_frame_buffer[5][v] |= (0b01111000 << a) & 0b01111000;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void DeInitSpectrum() {
|
|
SetF(initialFreq);
|
|
ToggleRX(false);
|
|
RestoreRegisters();
|
|
isInitialized = false;
|
|
}
|
|
|
|
static void OnKeyDown(uint8_t key) {
|
|
switch (key) {
|
|
case KEY_3:
|
|
SelectNearestPreset(true);
|
|
break;
|
|
case KEY_9:
|
|
SelectNearestPreset(false);
|
|
break;
|
|
case KEY_1:
|
|
UpdateScanStep(true);
|
|
break;
|
|
case KEY_7:
|
|
UpdateScanStep(false);
|
|
break;
|
|
case KEY_2:
|
|
UpdateFreqChangeStep(true);
|
|
break;
|
|
case KEY_8:
|
|
UpdateFreqChangeStep(false);
|
|
break;
|
|
case KEY_UP:
|
|
UpdateCurrentFreq(true);
|
|
break;
|
|
case KEY_DOWN:
|
|
UpdateCurrentFreq(false);
|
|
break;
|
|
case KEY_SIDE1:
|
|
Blacklist();
|
|
break;
|
|
case KEY_STAR:
|
|
UpdateRssiTriggerLevel(true);
|
|
break;
|
|
case KEY_F:
|
|
UpdateRssiTriggerLevel(false);
|
|
break;
|
|
case KEY_5:
|
|
FreqInput();
|
|
break;
|
|
case KEY_0:
|
|
ToggleModulation();
|
|
break;
|
|
case KEY_6:
|
|
ToggleListeningBW();
|
|
break;
|
|
case KEY_4:
|
|
ToggleStepsCount();
|
|
break;
|
|
case KEY_SIDE2:
|
|
ToggleBacklight();
|
|
break;
|
|
case KEY_PTT:
|
|
SetState(STILL);
|
|
TuneToPeak();
|
|
settings.rssiTriggerLevel = 120;
|
|
break;
|
|
case KEY_MENU:
|
|
break;
|
|
case KEY_EXIT:
|
|
if (menuState) {
|
|
menuState = 0;
|
|
break;
|
|
}
|
|
DeInitSpectrum();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void OnKeyDownFreqInput(uint8_t key) {
|
|
switch (key) {
|
|
case KEY_0:
|
|
case KEY_1:
|
|
case KEY_2:
|
|
case KEY_3:
|
|
case KEY_4:
|
|
case KEY_5:
|
|
case KEY_6:
|
|
case KEY_7:
|
|
case KEY_8:
|
|
case KEY_9:
|
|
case KEY_STAR:
|
|
UpdateFreqInput(key);
|
|
break;
|
|
case KEY_EXIT:
|
|
if (freqInputIndex == 0) {
|
|
SetState(previousState);
|
|
break;
|
|
}
|
|
UpdateFreqInput(key);
|
|
break;
|
|
case KEY_MENU:
|
|
if (tempFreq < F_MIN || tempFreq > F_MAX) {
|
|
break;
|
|
}
|
|
SetState(previousState);
|
|
currentFreq = tempFreq;
|
|
if (currentState == SPECTRUM) {
|
|
ResetBlacklist();
|
|
RelaunchScan();
|
|
} else {
|
|
SetF(currentFreq);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void OnKeyDownStill(key_code_t key) {
|
|
switch (key) {
|
|
case KEY_3:
|
|
break;
|
|
case KEY_9:
|
|
break;
|
|
case KEY_UP:
|
|
if (menuState) {
|
|
UpdateRegMenuValue(registerSpecs[menuState], true);
|
|
break;
|
|
}
|
|
UpdateCurrentFreqStill(true);
|
|
break;
|
|
case KEY_DOWN:
|
|
if (menuState) {
|
|
UpdateRegMenuValue(registerSpecs[menuState], false);
|
|
break;
|
|
}
|
|
UpdateCurrentFreqStill(false);
|
|
break;
|
|
case KEY_STAR:
|
|
UpdateRssiTriggerLevel(true);
|
|
break;
|
|
case KEY_F:
|
|
UpdateRssiTriggerLevel(false);
|
|
break;
|
|
case KEY_5:
|
|
FreqInput();
|
|
break;
|
|
case KEY_0:
|
|
ToggleModulation();
|
|
break;
|
|
case KEY_6:
|
|
ToggleListeningBW();
|
|
break;
|
|
case KEY_SIDE1:
|
|
monitorMode = !monitorMode;
|
|
break;
|
|
case KEY_SIDE2:
|
|
ToggleBacklight();
|
|
break;
|
|
case KEY_PTT:
|
|
// start transmit
|
|
UpdateBatteryInfo();
|
|
if (g_battery_display_level != 6 && IsTXAllowed()) {
|
|
ToggleTX(true);
|
|
}
|
|
redrawScreen = true;
|
|
break;
|
|
case KEY_MENU:
|
|
if (menuState == ARRAY_SIZE(registerSpecs) - 1) {
|
|
menuState = 1;
|
|
} else {
|
|
menuState++;
|
|
}
|
|
redrawScreen = true;
|
|
break;
|
|
case KEY_EXIT:
|
|
if (menuState) {
|
|
menuState = 0;
|
|
break;
|
|
}
|
|
SetState(SPECTRUM);
|
|
monitorMode = false;
|
|
RelaunchScan();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void OnKeysReleased() {
|
|
if (isTransmitting) {
|
|
ToggleTX(false);
|
|
}
|
|
}
|
|
|
|
static void RenderFreqInput() {
|
|
UI_PrintString(freqInputString, 2, 127, 0, 8, true);
|
|
}
|
|
|
|
static void RenderStatus() {
|
|
memset(g_status_line, 0, sizeof(g_status_line));
|
|
DrawStatus();
|
|
ST7565_BlitStatusLine();
|
|
}
|
|
|
|
static void RenderSpectrum() {
|
|
DrawTicks();
|
|
DrawArrow(peak.i << settings.stepsCount);
|
|
DrawSpectrum();
|
|
DrawRssiTriggerLevel();
|
|
DrawF(peak.f);
|
|
DrawNums();
|
|
}
|
|
|
|
static void RenderStill() {
|
|
DrawF(fMeasure);
|
|
|
|
const uint8_t METER_PAD_LEFT = 3;
|
|
|
|
for (int i = 0; i < 121; i++) {
|
|
if (i % 10 == 0) {
|
|
g_frame_buffer[2][i + METER_PAD_LEFT] = 0b11000000;
|
|
} else {
|
|
g_frame_buffer[2][i + METER_PAD_LEFT] = 0b01000000;
|
|
}
|
|
}
|
|
|
|
uint8_t x = Rssi2PX(scanInfo.rssi, 0, 121);
|
|
for (int i = 0; i < x; ++i) {
|
|
if (i % 5 && i / 5 < x / 5) {
|
|
g_frame_buffer[2][i + METER_PAD_LEFT] |= 0b00011100;
|
|
}
|
|
}
|
|
|
|
int dbm = Rssi2DBm(scanInfo.rssi);
|
|
uint8_t s = DBm2S(dbm);
|
|
if (s < 10) {
|
|
sprintf(String, "S%u", s);
|
|
} else {
|
|
sprintf(String, "S9+%u0", s - 9);
|
|
}
|
|
UI_PrintStringSmallest(String, 4, 10, false, true);
|
|
sprintf(String, "%d dBm", dbm);
|
|
UI_PrintStringSmallest(String, 32, 10, false, true);
|
|
|
|
if (isTransmitting) {
|
|
uint8_t afDB = BK4819_ReadRegister(0x6F) & 0b1111111;
|
|
uint8_t afPX = ConvertDomain(afDB, 26, 194, 0, 121);
|
|
for (int i = 0; i < afPX; ++i) {
|
|
g_frame_buffer[3][i + METER_PAD_LEFT] |= 0b00000011;
|
|
}
|
|
}
|
|
|
|
if (!monitorMode) {
|
|
uint8_t x = Rssi2PX(settings.rssiTriggerLevel, 0, 121);
|
|
g_frame_buffer[2][METER_PAD_LEFT + x - 1] |= 0b01000001;
|
|
g_frame_buffer[2][METER_PAD_LEFT + x] = 0b01111111;
|
|
g_frame_buffer[2][METER_PAD_LEFT + x + 1] |= 0b01000001;
|
|
}
|
|
|
|
const uint8_t PAD_LEFT = 4;
|
|
const uint8_t CELL_WIDTH = 30;
|
|
uint8_t offset = PAD_LEFT;
|
|
uint8_t row = 3;
|
|
|
|
for (int i = 0, idx = 1; idx <= 7; ++i, ++idx) {
|
|
if (idx == 5) {
|
|
row += 2;
|
|
i = 0;
|
|
}
|
|
offset = PAD_LEFT + i * CELL_WIDTH;
|
|
if (menuState == idx) {
|
|
for (int j = 0; j < CELL_WIDTH; ++j) {
|
|
g_frame_buffer[row][j + offset] = 0xFF;
|
|
g_frame_buffer[row + 1][j + offset] = 0xFF;
|
|
}
|
|
}
|
|
RegisterSpec s = registerSpecs[idx];
|
|
sprintf(String, "%s", s.name);
|
|
UI_PrintStringSmallest(String, offset + 2, row * 8 + 2, false,
|
|
menuState != idx);
|
|
sprintf(String, "%u", GetRegValue(s));
|
|
UI_PrintStringSmallest(String, offset + 2, (row + 1) * 8 + 1, false,
|
|
menuState != idx);
|
|
}
|
|
}
|
|
|
|
static void Render() {
|
|
memset(g_frame_buffer, 0, sizeof(g_frame_buffer));
|
|
|
|
switch (currentState) {
|
|
case SPECTRUM:
|
|
RenderSpectrum();
|
|
break;
|
|
case FREQ_INPUT:
|
|
RenderFreqInput();
|
|
break;
|
|
case STILL:
|
|
RenderStill();
|
|
break;
|
|
}
|
|
|
|
ST7565_BlitFullScreen();
|
|
}
|
|
|
|
bool HandleUserInput() {
|
|
kbd.prev = kbd.current;
|
|
kbd.current = GetKey();
|
|
|
|
if (kbd.current == KEY_INVALID) {
|
|
kbd.counter = 0;
|
|
OnKeysReleased();
|
|
return true;
|
|
}
|
|
|
|
if (kbd.current == kbd.prev && kbd.counter <= 20) {
|
|
kbd.counter++;
|
|
SYSTEM_DelayMs(10);
|
|
}
|
|
|
|
if (kbd.counter == 4 || kbd.counter > 20) {
|
|
switch (currentState) {
|
|
case SPECTRUM:
|
|
OnKeyDown(kbd.current);
|
|
break;
|
|
case FREQ_INPUT:
|
|
OnKeyDownFreqInput(kbd.current);
|
|
break;
|
|
case STILL:
|
|
OnKeyDownStill(kbd.current);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void Scan() {
|
|
if (blacklist[scanInfo.i]) {
|
|
return;
|
|
}
|
|
SetF(scanInfo.f);
|
|
Measure();
|
|
UpdateScanInfo();
|
|
}
|
|
|
|
static void NextScanStep() {
|
|
++peak.t;
|
|
++scanInfo.i;
|
|
scanInfo.f += scanInfo.scanStep;
|
|
}
|
|
|
|
static void UpdateScan() {
|
|
Scan();
|
|
|
|
if (scanInfo.i < scanInfo.measurementsCount) {
|
|
NextScanStep();
|
|
return;
|
|
}
|
|
|
|
MoveHistory();
|
|
|
|
redrawScreen = true;
|
|
preventKeypress = false;
|
|
|
|
UpdatePeakInfo();
|
|
if (IsPeakOverLevel()) {
|
|
ToggleRX(true);
|
|
TuneToPeak();
|
|
return;
|
|
}
|
|
|
|
newScanStart = true;
|
|
}
|
|
|
|
static void UpdateStill() {
|
|
Measure();
|
|
redrawScreen = true;
|
|
preventKeypress = false;
|
|
|
|
peak.rssi = scanInfo.rssi;
|
|
AutoTriggerLevel();
|
|
|
|
ToggleRX(IsPeakOverLevel() || monitorMode);
|
|
}
|
|
|
|
static void UpdateListening() {
|
|
preventKeypress = false;
|
|
if (!isListening) {
|
|
ToggleRX(true);
|
|
}
|
|
if (currentState == STILL) {
|
|
listenT = 0;
|
|
}
|
|
if (listenT) {
|
|
listenT--;
|
|
SYSTEM_DelayMs(1);
|
|
return;
|
|
}
|
|
|
|
if (currentState == SPECTRUM) {
|
|
BK4819_WriteRegister(0x43, GetBWRegValueForScan());
|
|
Measure();
|
|
BK4819_WriteRegister(0x43, GetBWRegValueForListen());
|
|
} else {
|
|
Measure();
|
|
}
|
|
|
|
peak.rssi = scanInfo.rssi;
|
|
redrawScreen = true;
|
|
|
|
if (IsPeakOverLevel() || monitorMode) {
|
|
listenT = 1000;
|
|
return;
|
|
}
|
|
|
|
ToggleRX(false);
|
|
newScanStart = true;
|
|
}
|
|
|
|
static void UpdateTransmitting() {}
|
|
|
|
static void Tick() {
|
|
if (!preventKeypress) {
|
|
HandleUserInput();
|
|
}
|
|
if (newScanStart) {
|
|
InitScan();
|
|
newScanStart = false;
|
|
}
|
|
if (isTransmitting) {
|
|
UpdateTransmitting();
|
|
} else if (isListening && currentState != FREQ_INPUT) {
|
|
UpdateListening();
|
|
} else {
|
|
if (currentState == SPECTRUM) {
|
|
UpdateScan();
|
|
} else if (currentState == STILL) {
|
|
UpdateStill();
|
|
}
|
|
}
|
|
if (++batteryUpdateTimer > 4096) {
|
|
batteryUpdateTimer = 0;
|
|
UpdateBatteryInfo();
|
|
redrawStatus = true;
|
|
}
|
|
if (redrawStatus) {
|
|
RenderStatus();
|
|
redrawStatus = false;
|
|
}
|
|
if (redrawScreen) {
|
|
Render();
|
|
redrawScreen = false;
|
|
}
|
|
}
|
|
|
|
static void AutomaticPresetChoose(uint32_t f) {
|
|
for (int i = 0; i < ARRAY_SIZE(freqPresets); ++i) {
|
|
FreqPreset p = freqPresets[i];
|
|
if (f >= p.fStart && f <= freqPresets[i].fEnd) {
|
|
ApplyPreset(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
void APP_RunSpectrum() {
|
|
BackupRegisters();
|
|
// TX here coz it always? set to active VFO
|
|
VFO_Info_t vfo = g_eeprom.vfo_info[g_eeprom.TX_CHANNEL];
|
|
initialFreq = vfo.p_rx->Frequency;
|
|
currentFreq = initialFreq;
|
|
settings.scanStepIndex = gStepSettingToIndex[vfo.STEP_SETTING];
|
|
settings.listenBw = vfo.CHANNEL_BANDWIDTH == BANDWIDTH_WIDE
|
|
? BANDWIDTH_WIDE
|
|
: BANDWIDTH_NARROW;
|
|
settings.modulationType = vfo.IsAM ? MOD_AM : MOD_FM;
|
|
|
|
AutomaticPresetChoose(currentFreq);
|
|
|
|
redrawStatus = true;
|
|
redrawScreen = false; // we will wait until scan done
|
|
newScanStart = true;
|
|
|
|
ToggleRX(true), ToggleRX(false); // hack to prevent noise when squelch off
|
|
SetModulation(settings.modulationType);
|
|
|
|
RelaunchScan();
|
|
|
|
for (int i = 0; i < 128; ++i) {
|
|
rssiHistory[i] = 0;
|
|
}
|
|
|
|
isInitialized = true;
|
|
|
|
while (isInitialized) {
|
|
Tick();
|
|
}
|
|
}
|