UV_K5_playground/src/spectrum_fagci/spectrum.hpp

375 wiersze
8.5 KiB
C++
Czysty Zwykły widok Historia

#pragma once
#include "radio.hpp"
#include "system.hpp"
#include "uv_k5_display.hpp"
typedef unsigned char u8;
2023-07-16 08:05:47 +00:00
typedef signed short i16;
typedef unsigned short u16;
2023-07-19 18:31:33 +00:00
typedef signed int i32;
typedef unsigned int u32;
2023-07-16 08:05:47 +00:00
typedef signed long long i64;
typedef unsigned long long u64;
template <Radio::CBK4819 &RadioDriver>
class CSpectrum {
public:
2023-07-21 10:50:18 +00:00
static constexpr auto ExitKey = 13;
static constexpr auto DrawingEndY = 42;
2023-07-19 18:31:33 +00:00
static constexpr auto BarPos = 5 * 128;
2023-07-26 17:22:47 +00:00
u8 rssiHistory[64] = {};
2023-07-20 20:13:29 +00:00
u8 measurementsCount = 32;
u8 rssiMin = 255;
u8 highestPeakX = 0;
u8 highestPeakT = 0;
u8 highestPeakRssi = 0;
u32 highestPeakF = 0;
2023-07-26 17:22:47 +00:00
u32 FStart, fMeasure;
2023-07-15 20:15:28 +00:00
CSpectrum()
: DisplayBuff(gDisplayBuffer), FontSmallNr(gSmallDigs),
2023-07-21 10:50:18 +00:00
Display(DisplayBuff), scanDelay(800), sampleZoom(2), scanStep(25_KHz),
frequencyChangeStep(100_KHz), rssiTriggerLevel(65), stickyPeakTrigger(false) {
Display.SetFont(&FontSmallNr);
};
inline bool ListenPeak() {
if (highestPeakRssi < rssiTriggerLevel) {
return false;
}
2023-07-15 10:55:12 +00:00
2023-07-26 17:22:47 +00:00
if (fMeasure != highestPeakF) {
fMeasure = highestPeakF;
RadioDriver.SetFrequency(fMeasure);
BK4819Write(0x47, u16OldAfSettings);
2023-07-26 17:22:47 +00:00
RadioDriver.ToggleAFDAC(true);
}
Listen(1000000);
2023-07-26 17:22:47 +00:00
highestPeakRssi = GetRssi();
rssiHistory[highestPeakX >> sampleZoom] = highestPeakRssi;
2023-07-18 07:30:15 +00:00
return true;
}
2023-07-18 07:30:15 +00:00
inline void Scan() {
u8 rssi = 0, rssiMax = 0;
u8 iPeak = 0;
2023-07-26 17:22:47 +00:00
u32 fPeak = currentFreq;
2023-07-18 07:30:15 +00:00
rssiMin = 255;
2023-07-26 17:22:47 +00:00
fMeasure = FStart;
RadioDriver.ToggleAFDAC(false);
BK4819Write(0x47, 0);
2023-07-18 07:30:15 +00:00
2023-07-19 18:31:33 +00:00
for (u8 i = 0; i < measurementsCount; ++i, fMeasure += scanStep) {
rssi = rssiHistory[i] = GetRssi(fMeasure);
2023-07-19 18:31:33 +00:00
if (rssi < rssiMin) {
rssiMin = rssi;
}
2023-07-18 07:30:15 +00:00
if (rssi > rssiMax) {
rssiMax = rssi;
fPeak = fMeasure;
iPeak = i;
}
}
++highestPeakT;
if (rssiMax > highestPeakRssi || highestPeakT >= (8 << sampleZoom)) {
highestPeakT = 0;
highestPeakRssi = rssiMax;
highestPeakX = iPeak << sampleZoom;
highestPeakF = fPeak;
}
2023-07-15 10:55:12 +00:00
}
2023-07-15 10:55:12 +00:00
inline void DrawSpectrum() {
2023-07-19 18:31:33 +00:00
for (u8 x = 0; x < 128; ++x) {
Display.DrawHLine(Rssi2Y(rssiHistory[x >> sampleZoom]), DrawingEndY, x);
}
2023-07-18 07:30:15 +00:00
}
inline void DrawNums() {
Display.SetCoursorXY(0, 0);
Display.PrintFixedDigitsNumber2(scanDelay, 0);
Display.SetCoursorXY(51, 0);
2023-07-19 18:31:33 +00:00
Display.PrintFixedDigitsNumber2(scanStep << (7 - sampleZoom));
Display.SetCoursorXY(58, 8);
2023-07-19 18:31:33 +00:00
Display.PrintFixedDigitsNumber2(scanStep);
2023-07-15 20:15:28 +00:00
Display.SetCoursorXY(107, 8);
2023-07-15 20:15:28 +00:00
Display.PrintFixedDigitsNumber2(highestPeakRssi, 0);
2023-07-16 08:05:47 +00:00
Display.SetCoursorXY(86, 0);
2023-07-19 18:31:33 +00:00
Display.PrintFixedDigitsNumber2(highestPeakF);
Display.SetCoursorXY(44, 48);
2023-07-19 18:31:33 +00:00
Display.PrintFixedDigitsNumber2(currentFreq);
Display.SetCoursorXY(100, 48);
Display.PrintFixedDigitsNumber2(frequencyChangeStep);
Display.SetCoursorXY(0, 8);
2023-07-16 08:05:47 +00:00
Display.PrintFixedDigitsNumber2(rssiTriggerLevel, 0);
2023-07-15 20:15:28 +00:00
}
inline void DrawRssiTriggerLevel() {
for (u8 x = 0; x < 128; x += stickyPeakTrigger ? 2 : 4) {
Display.DrawLine(x, x + 2, Rssi2Y(rssiTriggerLevel));
}
}
2023-07-15 10:55:12 +00:00
inline void DrawTicks() {
u32 f = modulo(FStart, 1_MHz);
2023-07-19 18:31:33 +00:00
u32 step = scanStep >> sampleZoom;
for (u8 i = 0; i < 128; ++i, f += step) {
2023-07-15 10:55:12 +00:00
u8 barValue = 0b00001000;
2023-07-19 18:31:33 +00:00
modulo(f, 100_KHz) < step && (barValue |= 0b00010000);
modulo(f, 500_KHz) < step && (barValue |= 0b00100000);
modulo(f, 1_MHz) < step && (barValue |= 0b11000000);
2023-07-15 10:55:12 +00:00
gDisplayBuffer[BarPos + i] |= barValue;
}
2023-07-15 10:55:12 +00:00
// center
gDisplayBuffer[BarPos + 64] |= 0b10101010;
2023-07-15 10:55:12 +00:00
}
inline void DrawArrow(u8 x) {
u8 *peakPos = gDisplayBuffer + BarPos + x;
2023-07-15 10:55:12 +00:00
x > 1 && (*(peakPos - 2) |= 0b01000000);
x > 0 && (*(peakPos - 1) |= 0b01100000);
(*(peakPos) |= 0b01110000);
x < 127 && (*(peakPos + 1) |= 0b01100000);
x < 128 && (*(peakPos + 2) |= 0b01000000);
}
void HandleUserInput() {
switch (lastButtonPressed) {
case 1:
2023-07-20 20:13:29 +00:00
UpdateScanDelay(200);
break;
case 7:
2023-07-20 20:13:29 +00:00
UpdateScanDelay(-200);
break;
2023-07-15 20:15:28 +00:00
case 2:
2023-07-19 18:31:33 +00:00
UpdateSampleZoom(1);
2023-07-15 20:15:28 +00:00
break;
case 8:
2023-07-19 18:31:33 +00:00
UpdateSampleZoom(-1);
break;
case 3:
UpdateRssiTriggerLevel(5);
break;
case 9:
2023-07-15 20:15:28 +00:00
UpdateRssiTriggerLevel(-5);
break;
2023-07-19 18:31:33 +00:00
case 4:
UpdateScanStep(-1);
UpdateSampleZoom(1);
2023-07-19 18:31:33 +00:00
break;
case 6:
UpdateScanStep(1);
UpdateSampleZoom(-1);
2023-07-15 20:15:28 +00:00
break;
case 11: // up
UpdateCurrentFreq(frequencyChangeStep);
break;
case 12: // down
UpdateCurrentFreq(-frequencyChangeStep);
break;
case 14:
UpdateFreqChangeStep(100_KHz);
break;
case 15:
UpdateFreqChangeStep(-100_KHz);
break;
2023-07-23 16:59:12 +00:00
case 5:
ToggleBacklight();
case 0:
stickyPeakTrigger = !stickyPeakTrigger;
OnUserInput();
}
}
2023-07-15 10:55:12 +00:00
void Render() {
2023-07-21 10:50:18 +00:00
DisplayBuff.ClearAll();
2023-07-15 10:55:12 +00:00
DrawTicks();
DrawArrow(highestPeakX);
DrawSpectrum();
2023-07-15 20:15:28 +00:00
DrawRssiTriggerLevel();
2023-07-18 07:30:15 +00:00
DrawNums();
FlushFramebufferToScreen();
2023-07-15 10:55:12 +00:00
}
void Update() {
if (bDisplayCleared) {
currentFreq = RadioDriver.GetFrequency();
2023-07-15 10:55:12 +00:00
OnUserInput();
u16OldAfSettings = BK4819Read(0x47);
BK4819Write(0x47, 0); // mute AF during scan
2023-07-15 10:55:12 +00:00
}
bDisplayCleared = false;
HandleUserInput();
if (!ListenPeak())
Scan();
2023-07-15 10:55:12 +00:00
}
2023-07-19 18:31:33 +00:00
void UpdateRssiTriggerLevel(i32 diff) {
rssiTriggerLevel = clamp(rssiTriggerLevel + diff, 10, 255);
2023-07-15 20:15:28 +00:00
OnUserInput();
}
2023-07-19 18:31:33 +00:00
void UpdateScanDelay(i32 diff) {
2023-07-20 20:13:29 +00:00
scanDelay = clamp(scanDelay + diff, 800, 3200);
2023-07-19 18:31:33 +00:00
OnUserInput();
}
void UpdateSampleZoom(i32 diff) {
2023-07-26 17:22:47 +00:00
sampleZoom = clamp(sampleZoom - diff, 1, 5);
2023-07-19 18:31:33 +00:00
measurementsCount = 1 << (7 - sampleZoom);
2023-07-15 10:55:12 +00:00
OnUserInput();
}
2023-07-16 08:05:47 +00:00
void UpdateCurrentFreq(i64 diff) {
2023-07-19 18:31:33 +00:00
currentFreq = clamp(currentFreq + diff, 18_MHz, 1300_MHz);
2023-07-15 10:55:12 +00:00
OnUserInput();
}
2023-07-19 18:31:33 +00:00
void UpdateScanStep(i32 diff) {
2023-07-20 20:13:29 +00:00
if (diff > 0 && scanStep < 25_KHz) {
scanStep <<= 1;
}
if (diff < 0 && scanStep > 6250_Hz) {
scanStep >>= 1;
}
OnUserInput();
2023-07-19 18:31:33 +00:00
}
void UpdateFreqChangeStep(i64 diff) {
frequencyChangeStep = clamp(frequencyChangeStep + diff, 100_KHz, 2_MHz);
OnUserInput();
}
2023-07-15 10:55:12 +00:00
inline void OnUserInput() {
2023-07-19 18:31:33 +00:00
u32 halfOfScanRange = scanStep << (6 - sampleZoom);
FStart = currentFreq - halfOfScanRange;
// reset peak
2023-07-18 07:30:15 +00:00
highestPeakT = 0;
highestPeakRssi = 0;
highestPeakX = 64;
2023-07-18 07:30:15 +00:00
highestPeakF = currentFreq;
2023-07-19 18:31:33 +00:00
DelayUs(90000);
}
void Handle() {
2023-07-21 10:50:18 +00:00
if (RadioDriver.IsLockedByOrgFw()) {
return;
}
2023-07-21 10:50:18 +00:00
if (!working) {
if (IsFlashLightOn()) {
working = true;
TurnOffFlashLight();
}
return;
}
lastButtonPressed = PollKeyboard();
if (lastButtonPressed == ExitKey) {
2023-07-21 10:50:18 +00:00
working = false;
RestoreParams();
return;
}
Update();
Render();
}
private:
void RestoreParams() {
if (!bDisplayCleared) {
bDisplayCleared = true;
DisplayBuff.ClearAll();
FlushFramebufferToScreen();
RadioDriver.SetFrequency(currentFreq);
BK4819Write(0x47, u16OldAfSettings); // set previous AF settings
}
}
inline void Listen(u32 duration) {
for (u8 i = 0; i < 16 && lastButtonPressed == 255; ++i) {
lastButtonPressed = PollKeyboard();
DelayUs(duration >> 4);
}
}
2023-07-26 17:22:47 +00:00
u8 GetRssi() {
if (!stickyPeakTrigger) {
// reset RSSI register
RadioDriver.ToggleRXDSP(false);
RadioDriver.ToggleRXDSP(true);
}
DelayUs(scanDelay);
return BK4819Read(0x67);
}
2023-07-26 17:22:47 +00:00
u8 GetRssi(u32 f) {
RadioDriver.SetFrequency(f);
return GetRssi();
}
2023-07-21 10:50:18 +00:00
inline bool IsFlashLightOn() { return GPIOC->DATA & GPIO_PIN_3; }
inline void TurnOffFlashLight() {
GPIOC->DATA &= ~GPIO_PIN_3;
gFlashLightStatus = 3;
}
2023-07-26 17:22:47 +00:00
inline void ToggleBacklight() { GPIOB->DATA ^= GPIO_PIN_6; }
2023-07-23 16:59:12 +00:00
2023-07-15 20:15:28 +00:00
inline u8 Rssi2Y(u8 rssi) {
2023-07-20 20:13:29 +00:00
return clamp(DrawingEndY - (rssi - rssiMin), 1, DrawingEndY);
2023-07-15 20:15:28 +00:00
}
2023-07-19 18:31:33 +00:00
inline i32 clamp(i32 v, i32 min, i32 max) {
if (v < min)
return min;
if (v > max)
return max;
return v;
}
2023-07-19 18:31:33 +00:00
inline u32 modulo(u32 num, u32 div) {
while (num >= div)
num -= div;
return num;
}
TUV_K5Display DisplayBuff;
const TUV_K5SmallNumbers FontSmallNr;
CDisplay<const TUV_K5Display> Display;
u8 lastButtonPressed;
u32 currentFreq;
u16 u16OldAfSettings;
2023-07-21 10:50:18 +00:00
u16 scanDelay;
2023-07-21 10:50:18 +00:00
u8 sampleZoom;
u32 scanStep;
u32 frequencyChangeStep;
2023-07-21 10:50:18 +00:00
u8 rssiTriggerLevel;
bool stickyPeakTrigger;
2023-07-21 10:50:18 +00:00
bool working = false;
bool bDisplayCleared = true;
};