diff --git a/README.md b/README.md index b9d057d..cf23dd5 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,41 @@ AFSK messenger with T9 typing support * if message is cleared use **EXIT** to exit messenger view * There is no timeout for the button. If you want to type letters located on the same button in a row, use an asterisk (*) to confirm the selected character +## src/spectrum_fagci ![auto release build](https://github.com/piotr022/UV_K5_playground/actions/workflows/c-cpp.yml/badge.svg) + +Spectrum scanner by [@fagci](https://github.com/fagci) + +![spectrum_fagci](./docs/spectrum_fagci.gif) + +[more videos](https://youtube.com/playlist?list=PL67vBSofOrHMyXOEhonOzQz5gJzsZFiDq) + +[download mod](https://github.com/piotr022/UV_K5_playground/releases/latest) + +Features: + +* no scan sound +* squelch by user input level +* 0.2 .. 3.2MHz frequency ranges +* ticks by frequency (100,500,1000KHz) +* catch signal peak frequency +* automatic frequency change step +* automatic scan step +* frequency blacklist to remove unwanted signals +* backlight control + +How to start: + +* set squelch level to 0 or go into monitor mode before enabling mod +* to enable spectum view press **flash light button** + +How to operate: + +* press **8** / **2** for zoom in / zoom out +* press and hold **3** / **9** to set squelch level +* press **5** to toggle backlight +* press **0** to remove frequency from sspectrum to scan +* press **EXIT** to disable spectrum view + ## src/spectrum ![auto release build](https://github.com/piotr022/UV_K5_playground/actions/workflows/c-cpp.yml/badge.svg) ![spectrum](./docs/spectrum.gif) **update** diff --git a/docs/spectrum_fagci.gif b/docs/spectrum_fagci.gif new file mode 100644 index 0000000..f98dbd4 Binary files /dev/null and b/docs/spectrum_fagci.gif differ diff --git a/libs/lcd/lcd.hpp b/libs/lcd/lcd.hpp index 1320faa..22ef78f 100644 --- a/libs/lcd/lcd.hpp +++ b/libs/lcd/lcd.hpp @@ -1,275 +1,286 @@ #pragma once -#include #include +#include -struct ILcd -{ - virtual void UpdateScreen() = 0; +struct ILcd { + virtual void UpdateScreen() = 0; }; -template -struct IBitmap -{ - constexpr IBitmap(const unsigned char *_pBuffStart) : pBuffStart(_pBuffStart){}; - static constexpr auto SizeX = _SizeX; - static constexpr auto SizeY = _SizeY; - static constexpr auto LineHeight = _LineHeight; - static constexpr auto Lines = _SizeX / _LineHeight; - static constexpr unsigned short GetCoursorPosition(unsigned char u8Line, unsigned char u8XPos) - { - return (u8Line * SizeX) + u8XPos; - } - - virtual bool GetPixel(unsigned char u8X, unsigned char u8Y) const = 0; - virtual void SetPixel(unsigned char u8X, unsigned char u8Y) const = 0; - virtual void *GetCoursorData(unsigned short u16CoursorPosition) const { return nullptr; } - virtual void ClearAll() = 0; - const unsigned char *pBuffStart; +template +struct IBitmap { + constexpr IBitmap(const unsigned char *_pBuffStart) + : pBuffStart(_pBuffStart){}; + static constexpr auto SizeX = _SizeX; + static constexpr auto SizeY = _SizeY; + static constexpr auto LineHeight = _LineHeight; + static constexpr auto Lines = _SizeX / _LineHeight; + static constexpr unsigned short GetCoursorPosition(unsigned char u8Line, + unsigned char u8XPos) { + return (u8Line * SizeX) + u8XPos; + } + + virtual bool GetPixel(unsigned char u8X, unsigned char u8Y) const = 0; + virtual void SetPixel(unsigned char u8X, unsigned char u8Y) const = 0; + virtual void *GetCoursorData(unsigned short u16CoursorPosition) const { + return nullptr; + } + virtual void ClearAll() = 0; + const unsigned char *pBuffStart; }; -struct IFont -{ - virtual bool GetPixel(char c8Character, unsigned char u8X, unsigned char u8Y) const = 0; - virtual unsigned char *GetRaw(char c8Character) const = 0; - virtual unsigned char GetSizeX(char c8Character) const = 0; - virtual unsigned char GetSizeY(char c8Character) const = 0; +struct IFont { + virtual bool GetPixel(char c8Character, unsigned char u8X, + unsigned char u8Y) const = 0; + virtual unsigned char *GetRaw(char c8Character) const = 0; + virtual unsigned char GetSizeX(char c8Character) const = 0; + virtual unsigned char GetSizeY(char c8Character) const = 0; }; -template -class CDisplay -{ +template class CDisplay { public: - constexpr CDisplay(BitmapType &_Bitmap) - : Bitmap(_Bitmap), pCurrentFont(nullptr), u16CoursorPosition(0) - { - } - - void SetCoursor(unsigned char u8Line, unsigned char u8X) const - { - u16CoursorPosition = (u8Line * Bitmap.SizeX) + u8X; - } - - void SetCoursorXY(unsigned char x, unsigned char y) const - { - u16CoursorPosition = x + (y << 4); - } - - void SetFont(const IFont *pFont) const - { - pCurrentFont = pFont; - } - - void DrawLine(int sx, int ex, int ny) - { - for (int i = sx; i <= ex; i++) - { - if (i < Bitmap.SizeX && ny < Bitmap.SizeY) - { - Bitmap.SetPixel(i, ny); - } - } - } - - void DrawHLine(int sy, int ey, int nx, bool bCropped = false) - { - for (int i = sy; i <= ey; i++) - { - if (i < Bitmap.SizeY && nx < Bitmap.SizeX && (!bCropped || i % 2)) - { - Bitmap.SetPixel(nx, i); - } - } - } - - void DrawCircle(unsigned char cx, unsigned char cy, unsigned int r, bool bFilled = false) - { - int x = 0; - int y = r; - int d = 3 - 2 * r; - if (!r) - return; - - while (y >= x) - { - // when bFilled is true, draw lines to fill the circle - if (bFilled) - { - DrawLine(cx - x, cx + x, cy - y); - DrawLine(cx - y, cx + y, cy - x); - DrawLine(cx - x, cx + x, cy + y); - DrawLine(cx - y, cx + y, cy + x); - } - else - { - if (cx + x < Bitmap.SizeX && cy + y < Bitmap.SizeY) - Bitmap.SetPixel(cx + x, cy - y); // 1st quarter - if (cx + y < Bitmap.SizeX && cy + x < Bitmap.SizeY) - Bitmap.SetPixel(cx + y, cy - x); // 2nd quarter - if (cx - x >= 0 && cy + y < Bitmap.SizeY) - Bitmap.SetPixel(cx - x, cy - y); // 3rd quarter - if (cx - y >= 0 && cy + x < Bitmap.SizeY) - Bitmap.SetPixel(cx - y, cy - x); // 4th quarter - if (cx + x < Bitmap.SizeX && cy + y < Bitmap.SizeY) - Bitmap.SetPixel(cx + x, cy + y); // 5th quarter - if (cx + y < Bitmap.SizeX && cy - x >= 0) - Bitmap.SetPixel(cx + y, cy + x); // 6th quarter - if (cx - x >= 0 && cy - y >= 0) - Bitmap.SetPixel(cx - x, cy + y); // 7th quarter - if (cx - y >= 0 && cy - x >= 0) - Bitmap.SetPixel(cx - y, cy + x); // 8th quarter - } - x++; - if (d < 0) - d += 4 * x + 6; - else - { - y--; - d += 4 * (x - y) + 10; - } - } - } - - void DrawRectangle(unsigned char sx, unsigned char sy, unsigned char width, unsigned char height, bool bFilled) - { - unsigned char maxX = (sx + width < Bitmap.SizeX) ? sx + width : Bitmap.SizeX; - unsigned char maxY = (sy + height < Bitmap.SizeY) ? sy + height : Bitmap.SizeY; - - // Draw vertical lines - for (unsigned char y = sy; y < maxY; y++) - { - Bitmap.SetPixel(sx, y); - Bitmap.SetPixel(maxX - 1, y); - } + constexpr CDisplay(BitmapType &_Bitmap) + : Bitmap(_Bitmap), pCurrentFont(nullptr), u16CoursorPosition(0) {} - // Draw horizontal lines - for (unsigned char x = sx; x < maxX; x++) - { - Bitmap.SetPixel(x, sy); - Bitmap.SetPixel(x, maxY - 1); - } + void SetCoursor(unsigned char u8Line, unsigned char u8X) const { + u16CoursorPosition = (u8Line * Bitmap.SizeX) + u8X; + } - // If filled, draw horizontal lines within the rectangle - if (bFilled) - { - for (unsigned char x = sx + 1; x < maxX - 1; x++) - { - for (unsigned char y = sy + 1; y < maxY - 1; y++) - { - Bitmap.SetPixel(x, y); - } - } - } - } + void SetCoursorXY(unsigned char x, unsigned char y) const { + u16CoursorPosition = x + (y << 4); + } - unsigned char PrintCharacter(const char c8Character) const - { - if (!pCurrentFont) - { - return 0; - } + void SetFont(const IFont *pFont) const { pCurrentFont = pFont; } - const auto *const pFontRawData = pCurrentFont->GetRaw(c8Character); - auto *pCoursorPosition = Bitmap.GetCoursorData(u16CoursorPosition); - auto const CopySize = pCurrentFont->GetSizeY(c8Character) * (BitmapType::LineHeight / 8); - if (pCoursorPosition && !(BitmapType::LineHeight % 8)) - { - if (pFontRawData) - memcpy(pCoursorPosition, pFontRawData, CopySize); - else - memset(pCoursorPosition, 0, CopySize); + void DrawLine(int sx, int ex, int ny) { + for (int i = sx; i <= ex; i++) { + if (i < Bitmap.SizeX && ny < Bitmap.SizeY) { + Bitmap.SetPixel(i, ny); } + } + } - u16CoursorPosition += pCurrentFont->GetSizeY(c8Character); - return 0; - } - - void Print(const char *C8String) const - { - for (unsigned char i = 0; i < strlen(C8String); i++) - { - PrintCharacter(C8String[i]); + void DrawHLine(int sy, int ey, int nx, bool bCropped = false) { + for (int i = sy; i <= ey; i++) { + if (i < Bitmap.SizeY && nx < Bitmap.SizeX && (!bCropped || i % 2)) { + Bitmap.SetPixel(nx, i); } - } - - unsigned char PrintFixedDigtsNumer(int s32Number, unsigned char u8Digts) - { - char U8NumBuff[32]; - memset(U8NumBuff, 0, sizeof(U8NumBuff)); - - char *pString = U8NumBuff + u8Digts; - *pString = '\0'; - - if (s32Number < 0) - { - U8NumBuff[0] = '-'; - s32Number = -s32Number; + } + } + + void DrawCircle(unsigned char cx, unsigned char cy, unsigned int r, + bool bFilled = false) { + int x = 0; + int y = r; + int d = 3 - 2 * r; + if (!r) + return; + + while (y >= x) { + // when bFilled is true, draw lines to fill the circle + if (bFilled) { + DrawLine(cx - x, cx + x, cy - y); + DrawLine(cx - y, cx + y, cy - x); + DrawLine(cx - x, cx + x, cy + y); + DrawLine(cx - y, cx + y, cy + x); + } else { + if (cx + x < Bitmap.SizeX && cy + y < Bitmap.SizeY) + Bitmap.SetPixel(cx + x, cy - y); // 1st quarter + if (cx + y < Bitmap.SizeX && cy + x < Bitmap.SizeY) + Bitmap.SetPixel(cx + y, cy - x); // 2nd quarter + if (cx - x >= 0 && cy + y < Bitmap.SizeY) + Bitmap.SetPixel(cx - x, cy - y); // 3rd quarter + if (cx - y >= 0 && cy + x < Bitmap.SizeY) + Bitmap.SetPixel(cx - y, cy - x); // 4th quarter + if (cx + x < Bitmap.SizeX && cy + y < Bitmap.SizeY) + Bitmap.SetPixel(cx + x, cy + y); // 5th quarter + if (cx + y < Bitmap.SizeX && cy - x >= 0) + Bitmap.SetPixel(cx + y, cy + x); // 6th quarter + if (cx - x >= 0 && cy - y >= 0) + Bitmap.SetPixel(cx - x, cy + y); // 7th quarter + if (cx - y >= 0 && cy - x >= 0) + Bitmap.SetPixel(cx - y, cy + x); // 8th quarter } - - while (u8Digts--) - { - *--pString = '0' + (s32Number % 10); - s32Number /= 10; + x++; + if (d < 0) + d += 4 * x + 6; + else { + y--; + d += 4 * (x - y) + 10; } - - Print(U8NumBuff); - return u8Digts * pCurrentFont->GetSizeX('0'); - } - - static constexpr int powersOfTen[9] = { - 1, // 10^0 - 10, // 10^1 - 100, // 10^2 - 1000, // 10^3 - 10000, // 10^4 - 100000, // 10^5 - 1000000, // 10^6 - 10000000, // 10^7 - 100000000 // 10^8 - }; - void PrintFixedDigitsNumber2(int s32Number, unsigned char u8DigsToCut = 2, unsigned char u8FixedDigtsCnt = 0) - { - char U8NumBuff[11] = {0}; // 9 digits, sign, and null terminator - int startIdx = 0; - bool isNegative = false; - - if (s32Number < 0) - { - PrintCharacter('-'); - // U8NumBuff[0] = '-'; - s32Number = -s32Number; - // isNegative = true; + } + } + + void DrawRectangle(unsigned char sx, unsigned char sy, unsigned char width, + unsigned char height, bool bFilled) { + unsigned char maxX = + (sx + width < Bitmap.SizeX) ? sx + width : Bitmap.SizeX; + unsigned char maxY = + (sy + height < Bitmap.SizeY) ? sy + height : Bitmap.SizeY; + + // Draw vertical lines + for (unsigned char y = sy; y < maxY; y++) { + Bitmap.SetPixel(sx, y); + Bitmap.SetPixel(maxX - 1, y); + } + + // Draw horizontal lines + for (unsigned char x = sx; x < maxX; x++) { + Bitmap.SetPixel(x, sy); + Bitmap.SetPixel(x, maxY - 1); + } + + // If filled, draw horizontal lines within the rectangle + if (bFilled) { + for (unsigned char x = sx + 1; x < maxX - 1; x++) { + for (unsigned char y = sy + 1; y < maxY - 1; y++) { + Bitmap.SetPixel(x, y); + } } + } + } - for (int i = 8; i >= u8DigsToCut; --i) // assuming powersOfTen is an array of powers of 10 - { - int digit = 0; - while (s32Number >= powersOfTen[i]) - { - s32Number -= powersOfTen[i]; - ++digit; - } - U8NumBuff[isNegative + (8 - i)] = '0' + digit; - - // We found the first non-zero digit - if (digit != 0 && startIdx == (isNegative ? 1 : 0)) - startIdx = isNegative + (8 - i); + unsigned char PrintCharacter(const char c8Character) const { + if (!pCurrentFont) { + return 0; + } + + const auto *const pFontRawData = pCurrentFont->GetRaw(c8Character); + auto *pCoursorPosition = Bitmap.GetCoursorData(u16CoursorPosition); + auto const CopySize = + pCurrentFont->GetSizeY(c8Character) * (BitmapType::LineHeight / 8); + if (pCoursorPosition && !(BitmapType::LineHeight % 8)) { + if (pFontRawData) + memcpy(pCoursorPosition, pFontRawData, CopySize); + else + memset(pCoursorPosition, 0, CopySize); + } + + u16CoursorPosition += pCurrentFont->GetSizeY(c8Character); + return 0; + } + + void Print(const char *C8String) const { + for (unsigned char i = 0; i < strlen(C8String); i++) { + PrintCharacter(C8String[i]); + } + } + + unsigned char PrintFixedDigtsNumer(int s32Number, unsigned char u8Digts) { + char U8NumBuff[32]; + memset(U8NumBuff, 0, sizeof(U8NumBuff)); + + char *pString = U8NumBuff + u8Digts; + *pString = '\0'; + + if (s32Number < 0) { + U8NumBuff[0] = '-'; + s32Number = -s32Number; + } + + while (u8Digts--) { + *--pString = '0' + (s32Number % 10); + s32Number /= 10; + } + + Print(U8NumBuff); + return u8Digts * pCurrentFont->GetSizeX('0'); + } + + static constexpr int powersOfTen[9] = { + 1, // 10^0 + 10, // 10^1 + 100, // 10^2 + 1000, // 10^3 + 10000, // 10^4 + 100000, // 10^5 + 1000000, // 10^6 + 10000000, // 10^7 + 100000000 // 10^8 + }; + void PrintFixedDigitsNumber2(int s32Number, unsigned char u8DigsToCut = 2, + unsigned char u8FixedDigtsCnt = 0) { + char U8NumBuff[11] = {0}; // 9 digits, sign, and null terminator + int startIdx = 0; + bool isNegative = false; + + if (s32Number < 0) { + PrintCharacter('-'); + // U8NumBuff[0] = '-'; + s32Number = -s32Number; + // isNegative = true; + } + + for (int i = 8; i >= u8DigsToCut; + --i) // assuming powersOfTen is an array of powers of 10 + { + int digit = 0; + while (s32Number >= powersOfTen[i]) { + s32Number -= powersOfTen[i]; + ++digit; } - - // If the number was 0, we write a single 0. - if (startIdx == (isNegative ? 1 : 0)) - U8NumBuff[isNegative] = '0'; - - // Print the string from the start index - if (u8FixedDigtsCnt) - { - startIdx = 9 - u8DigsToCut - u8FixedDigtsCnt; + U8NumBuff[isNegative + (8 - i)] = '0' + digit; + + // We found the first non-zero digit + if (digit != 0 && startIdx == (isNegative ? 1 : 0)) + startIdx = isNegative + (8 - i); + } + + // If the number was 0, we write a single 0. + if (startIdx == (isNegative ? 1 : 0)) + U8NumBuff[isNegative] = '0'; + + // Print the string from the start index + if (u8FixedDigtsCnt) { + startIdx = 9 - u8DigsToCut - u8FixedDigtsCnt; + } + + Print(U8NumBuff + startIdx); + } + void PrintFixedDigitsNumber3(int s32Number, unsigned char u8DigsToCut = 2, + unsigned char u8FixedDigtsCnt = 0, + unsigned char pointAt = 128) { + char U8NumBuff[11] = {0}; // 9 digits, sign, and null terminator + unsigned char startIdx = 0; + + for (unsigned char i = 8; i >= u8DigsToCut; --i) { + int digit = 0; + while (s32Number >= powersOfTen[i]) { + s32Number -= powersOfTen[i]; + ++digit; } - - Print(U8NumBuff + startIdx); - } + U8NumBuff[8 - i] = '0' + digit; + + // We found the first non-zero digit + if (digit != 0 && startIdx == 0) + startIdx = 8 - i; + } + + // If the number was 0, we write a single 0. + /* if (startIdx == 0) + U8NumBuff[0] = '0'; */ + + // Print the string from the start index + if (u8FixedDigtsCnt) { + startIdx = 9 - u8DigsToCut - u8FixedDigtsCnt; + } + + const char *str = U8NumBuff + startIdx; + const char len = strlen(str); + const char dot[1] = {64}; + for (unsigned char i = 0; i < len; i++) { + if (pointAt == len - i) { + u16CoursorPosition++; + auto *pCoursorPosition = Bitmap.GetCoursorData(u16CoursorPosition); + memcpy(pCoursorPosition, dot, 1); + u16CoursorPosition++; + } + PrintCharacter(str[i]); + } + } private: - const BitmapType &Bitmap; - mutable const IFont *pCurrentFont; - mutable unsigned short u16CoursorPosition; + const BitmapType &Bitmap; + mutable const IFont *pCurrentFont; + mutable unsigned short u16CoursorPosition; }; diff --git a/src/spectrum_fagci/main.cpp b/src/spectrum_fagci/main.cpp index 1f9f326..314c4f4 100644 --- a/src/spectrum_fagci/main.cpp +++ b/src/spectrum_fagci/main.cpp @@ -1,13 +1,12 @@ #include "hardware/hardware.hpp" +#include "radio.hpp" #include "registers.hpp" #include "spectrum.hpp" #include "system.hpp" -#include "radio.hpp" #include "uv_k5_display.hpp" #include Hardware::THardware Hw; - Radio::CBK4819 RadioDriver; CSpectrum Spectrum; diff --git a/src/spectrum_fagci/spectrum.hpp b/src/spectrum_fagci/spectrum.hpp index ab7869b..508e35b 100644 --- a/src/spectrum_fagci/spectrum.hpp +++ b/src/spectrum_fagci/spectrum.hpp @@ -1,206 +1,209 @@ #pragma once #include "radio.hpp" #include "system.hpp" +#include "types.hpp" #include "uv_k5_display.hpp" -typedef unsigned char u8; -typedef signed short i16; -typedef unsigned short u16; -typedef signed int i32; -typedef unsigned int u32; -typedef signed long long i64; -typedef unsigned long long u64; - -template -class CSpectrum { +template class CSpectrum { public: static constexpr auto ExitKey = 13; static constexpr auto DrawingEndY = 42; static constexpr auto BarPos = 5 * 128; - u8 rssiHistory[64] = {}; - u8 measurementsCount = 32; + u8 rssiHistory[128] = {}; + u32 fMeasure; + + u8 peakT = 0; + u8 peakRssi = 0; + u8 peakI = 0; + u32 peakF = 0; u8 rssiMin = 255; - u8 highestPeakX = 0; - u8 highestPeakT = 0; - u8 highestPeakRssi = 0; - u32 highestPeakF = 0; - u32 FStart, fMeasure; + + u16 scanDelay = 1200; + + bool resetBlacklist = false; CSpectrum() - : DisplayBuff(gDisplayBuffer), FontSmallNr(gSmallDigs), - Display(DisplayBuff), scanDelay(800), sampleZoom(2), scanStep(25_KHz), - frequencyChangeStep(100_KHz), rssiTriggerLevel(65), stickyPeakTrigger(false) { + : DisplayBuff(gDisplayBuffer), Display(DisplayBuff), + FontSmallNr(gSmallDigs), frequencyChangeStep(400_KHz), bwMul(2), + rssiTriggerLevel(60) { Display.SetFont(&FontSmallNr); }; - inline bool ListenPeak() { - if (highestPeakRssi < rssiTriggerLevel) { - return false; - } - - if (fMeasure != highestPeakF) { - fMeasure = highestPeakF; - RadioDriver.SetFrequency(fMeasure); - BK4819Write(0x47, u16OldAfSettings); - RadioDriver.ToggleAFDAC(true); - } - - Listen(1000000); - - highestPeakRssi = GetRssi(); - rssiHistory[highestPeakX >> sampleZoom] = highestPeakRssi; - - return true; - } - - inline void Scan() { + void Scan() { u8 rssi = 0, rssiMax = 0; u8 iPeak = 0; u32 fPeak = currentFreq; - rssiMin = 255; - fMeasure = FStart; + fMeasure = GetFStart(); RadioDriver.ToggleAFDAC(false); - BK4819Write(0x47, 0); + MuteAF(); - for (u8 i = 0; i < measurementsCount; ++i, fMeasure += scanStep) { - rssi = rssiHistory[i] = GetRssi(fMeasure); - if (rssi < rssiMin) { - rssiMin = rssi; + u16 scanStep = GetScanStep(); + u8 measurementsCount = GetMeasurementsCount(); + + for (u8 i = 0; + i < measurementsCount && (PollKeyboard() == 255 || resetBlacklist); + ++i, fMeasure += scanStep) { + if (!resetBlacklist && rssiHistory[i] == 255) { + continue; } + RadioDriver.SetFrequency(fMeasure); + rssi = rssiHistory[i] = GetRssi(); if (rssi > rssiMax) { rssiMax = rssi; fPeak = fMeasure; iPeak = i; } + if (rssi < rssiMin) { + rssiMin = rssi; + } } - - ++highestPeakT; - if (rssiMax > highestPeakRssi || highestPeakT >= (8 << sampleZoom)) { - highestPeakT = 0; - highestPeakRssi = rssiMax; - highestPeakX = iPeak << sampleZoom; - highestPeakF = fPeak; + resetBlacklist = false; + ++peakT; + + if (rssiMax > peakRssi || peakT >= 16) { + peakT = 0; + peakRssi = rssiMax; + peakF = fPeak; + peakI = iPeak; } } - inline void DrawSpectrum() { + void DrawSpectrum() { for (u8 x = 0; x < 128; ++x) { - Display.DrawHLine(Rssi2Y(rssiHistory[x >> sampleZoom]), DrawingEndY, x); + auto v = rssiHistory[x >> BWMul2XDiv()]; + if (v != 255) { + Display.DrawHLine(Rssi2Y(v), DrawingEndY, x); + } } } - inline void DrawNums() { + void DrawNums() { Display.SetCoursorXY(0, 0); - Display.PrintFixedDigitsNumber2(scanDelay, 0); + Display.PrintFixedDigitsNumber3(scanDelay, 2, 2, 1); - Display.SetCoursorXY(51, 0); - Display.PrintFixedDigitsNumber2(scanStep << (7 - sampleZoom)); + Display.SetCoursorXY(112, 0); + Display.PrintFixedDigitsNumber3(GetBW(), 4, 2, 1); - Display.SetCoursorXY(58, 8); - Display.PrintFixedDigitsNumber2(scanStep); + /* Display.SetCoursorXY(0, 0); + Display.PrintFixedDigitsNumber2(rssiMinV, 0); */ - Display.SetCoursorXY(107, 8); - Display.PrintFixedDigitsNumber2(highestPeakRssi, 0); + Display.SetCoursorXY(44, 0); + Display.PrintFixedDigitsNumber3(peakF, 2, 6, 3); - Display.SetCoursorXY(86, 0); - Display.PrintFixedDigitsNumber2(highestPeakF); + Display.SetCoursorXY(0, 48); + Display.PrintFixedDigitsNumber3(GetFStart(), 4, 4, 1); - Display.SetCoursorXY(44, 48); - Display.PrintFixedDigitsNumber2(currentFreq); + Display.SetCoursorXY(98, 48); + Display.PrintFixedDigitsNumber3(GetFEnd(), 4, 4, 1); - Display.SetCoursorXY(100, 48); - Display.PrintFixedDigitsNumber2(frequencyChangeStep); + Display.SetCoursorXY(57, 48); + Display.PrintFixedDigitsNumber3(frequencyChangeStep, 4, 2, 1); - Display.SetCoursorXY(0, 8); - Display.PrintFixedDigitsNumber2(rssiTriggerLevel, 0); + /* Display.SetCoursorXY(0, 8); + Display.PrintFixedDigitsNumber2(rssiMaxV, 0); */ } - inline void DrawRssiTriggerLevel() { - for (u8 x = 0; x < 128; x += stickyPeakTrigger ? 2 : 4) { - Display.DrawLine(x, x + 2, Rssi2Y(rssiTriggerLevel)); + void DrawRssiTriggerLevel() { + u8 y = Rssi2Y(rssiTriggerLevel); + for (u8 x = 0; x < 126; x += 4) { + Display.DrawLine(x, x + 2, y); } } - inline void DrawTicks() { - u32 f = modulo(FStart, 1_MHz); - u32 step = scanStep >> sampleZoom; - for (u8 i = 0; i < 128; ++i, f += step) { - u8 barValue = 0b00001000; - modulo(f, 100_KHz) < step && (barValue |= 0b00010000); - modulo(f, 500_KHz) < step && (barValue |= 0b00100000); - modulo(f, 1_MHz) < step && (barValue |= 0b11000000); + void DrawTicks() { + // center + gDisplayBuffer[BarPos + 64] = 0b00111000; + } - gDisplayBuffer[BarPos + i] |= barValue; + void DrawArrow(u8 x) { + for (signed i = -2; i <= 2; ++i) { + signed v = x + i; + if (!(v & 128)) { + gDisplayBuffer[BarPos + v] |= (0b01111000 << abs(i)) & 0b01111000; + } } - - // center - gDisplayBuffer[BarPos + 64] |= 0b10101010; } - inline void DrawArrow(u8 x) { - u8 *peakPos = gDisplayBuffer + BarPos + x; - x > 1 && (*(peakPos - 2) |= 0b01000000); - x > 0 && (*(peakPos - 1) |= 0b01100000); - (*(peakPos) |= 0b01110000); - x < 127 && (*(peakPos + 1) |= 0b01100000); - x < 128 && (*(peakPos + 2) |= 0b01000000); + void OnKey(u8 key) { + switch (key) { + case 14: + UpdateRssiTriggerLevel(1); + DelayMs(90); + break; + case 15: + UpdateRssiTriggerLevel(-1); + DelayMs(90); + break; + } } - void HandleUserInput() { - switch (lastButtonPressed) { + void OnKeyDown(u8 key) { + switch (key) { case 1: - UpdateScanDelay(200); + if (scanDelay < 8000) { + scanDelay += 200; + rssiMin = 255; + } break; case 7: - UpdateScanDelay(-200); - break; - case 2: - UpdateSampleZoom(1); - break; - case 8: - UpdateSampleZoom(-1); + if (scanDelay > 800) { + scanDelay -= 200; + rssiMin = 255; + } break; case 3: - UpdateRssiTriggerLevel(5); + UpdateBWMul(1); + resetBlacklist = true; break; case 9: - UpdateRssiTriggerLevel(-5); + UpdateBWMul(-1); + resetBlacklist = true; break; - case 4: - UpdateScanStep(-1); - UpdateSampleZoom(1); + case 2: + UpdateFreqChangeStep(100_KHz); break; - case 6: - UpdateScanStep(1); - UpdateSampleZoom(-1); + case 8: + UpdateFreqChangeStep(-100_KHz); break; case 11: // up UpdateCurrentFreq(frequencyChangeStep); + resetBlacklist = true; break; case 12: // down UpdateCurrentFreq(-frequencyChangeStep); - break; - case 14: - UpdateFreqChangeStep(100_KHz); - break; - case 15: - UpdateFreqChangeStep(-100_KHz); + resetBlacklist = true; break; case 5: ToggleBacklight(); + break; case 0: - stickyPeakTrigger = !stickyPeakTrigger; - OnUserInput(); + Blacklist(); + break; } + ResetPeak(); + } + + bool HandleUserInput() { + btnPrev = btn; + btn = PollKeyboard(); + if (btn == ExitKey) { + DeInit(); + return false; + } + OnKey(btn); + if (btn != 255 && btnPrev == 255) { + OnKeyDown(btn); + } + return true; } void Render() { DisplayBuff.ClearAll(); DrawTicks(); - DrawArrow(highestPeakX); + DrawArrow(peakI << BWMul2XDiv()); DrawSpectrum(); DrawRssiTriggerLevel(); DrawNums(); @@ -208,167 +211,169 @@ class CSpectrum { } void Update() { - if (bDisplayCleared) { - currentFreq = RadioDriver.GetFrequency(); - OnUserInput(); - u16OldAfSettings = BK4819Read(0x47); - BK4819Write(0x47, 0); // mute AF during scan + if (peakRssi >= rssiTriggerLevel) { + Listen(1600); + return; } - bDisplayCleared = false; - - HandleUserInput(); - - if (!ListenPeak()) - Scan(); + Scan(); } void UpdateRssiTriggerLevel(i32 diff) { - rssiTriggerLevel = clamp(rssiTriggerLevel + diff, 10, 255); - OnUserInput(); - } - - void UpdateScanDelay(i32 diff) { - scanDelay = clamp(scanDelay + diff, 800, 3200); - OnUserInput(); + if ((diff > 0 && rssiTriggerLevel < 255) || + (diff < 0 && rssiTriggerLevel > 0)) { + rssiTriggerLevel += diff; + } } - void UpdateSampleZoom(i32 diff) { - sampleZoom = clamp(sampleZoom - diff, 1, 5); - measurementsCount = 1 << (7 - sampleZoom); - OnUserInput(); + void UpdateBWMul(i32 diff) { + if ((diff > 0 && bwMul < 4) || (diff < 0 && bwMul > 0)) { + bwMul += diff; + } + frequencyChangeStep = 100_KHz << bwMul; } void UpdateCurrentFreq(i64 diff) { - currentFreq = clamp(currentFreq + diff, 18_MHz, 1300_MHz); - OnUserInput(); - } - - void UpdateScanStep(i32 diff) { - if (diff > 0 && scanStep < 25_KHz) { - scanStep <<= 1; - } - if (diff < 0 && scanStep > 6250_Hz) { - scanStep >>= 1; + if ((diff > 0 && currentFreq < 1300_MHz) || + (diff < 0 && currentFreq > 18_MHz)) { + currentFreq += diff; } - OnUserInput(); } void UpdateFreqChangeStep(i64 diff) { frequencyChangeStep = clamp(frequencyChangeStep + diff, 100_KHz, 2_MHz); - OnUserInput(); } - inline void OnUserInput() { - u32 halfOfScanRange = scanStep << (6 - sampleZoom); - FStart = currentFreq - halfOfScanRange; - - // reset peak - highestPeakT = 0; - highestPeakRssi = 0; - highestPeakX = 64; - highestPeakF = currentFreq; - - DelayUs(90000); - } + void Blacklist() { rssiHistory[peakI] = 255; } void Handle() { if (RadioDriver.IsLockedByOrgFw()) { return; } - if (!working) { - if (IsFlashLightOn()) { - working = true; - TurnOffFlashLight(); - } - return; + if (!isInitialized && IsFlashLightOn()) { + TurnOffFlashLight(); + Init(); } - lastButtonPressed = PollKeyboard(); - if (lastButtonPressed == ExitKey) { - working = false; - RestoreParams(); - return; + if (isInitialized && HandleUserInput()) { + Update(); + Render(); } - Update(); - Render(); } private: - void RestoreParams() { - if (!bDisplayCleared) { - bDisplayCleared = true; - DisplayBuff.ClearAll(); - FlushFramebufferToScreen(); - RadioDriver.SetFrequency(currentFreq); - BK4819Write(0x47, u16OldAfSettings); // set previous AF settings - } + void Init() { + currentFreq = RadioDriver.GetFrequency(); + oldAFSettings = BK4819Read(0x47); + oldBWSettings = BK4819Read(0x43); + MuteAF(); + SetWideBW(); + isInitialized = true; } - inline void Listen(u32 duration) { - for (u8 i = 0; i < 16 && lastButtonPressed == 255; ++i) { - lastButtonPressed = PollKeyboard(); - DelayUs(duration >> 4); + void DeInit() { + DisplayBuff.ClearAll(); + FlushFramebufferToScreen(); + RadioDriver.SetFrequency(currentFreq); + RestoreOldAFSettings(); + BK4819Write(0x43, oldBWSettings); + isInitialized = false; + } + + void ResetPeak() { + peakRssi = 0; + peakF = currentFreq; + peakT = 0; + } + + void SetWideBW() { + auto Reg = BK4819Read(0x43); + Reg &= ~(0b11 << 4); + BK4819Write(0x43, Reg | (0b11 << 4)); + } + void MuteAF() { BK4819Write(0x47, 0); } + void RestoreOldAFSettings() { BK4819Write(0x47, oldAFSettings); } + + void Listen(u16 durationMs) { + if (fMeasure != peakF) { + fMeasure = peakF; + RadioDriver.SetFrequency(fMeasure); + RestoreOldAFSettings(); + RadioDriver.ToggleAFDAC(true); } + for (u8 i = 0; i < 16 && PollKeyboard() == 255; ++i) { + DelayMs(durationMs >> 4); + } + peakRssi = rssiHistory[peakI] = GetRssi(); } - u8 GetRssi() { - if (!stickyPeakTrigger) { - // reset RSSI register - RadioDriver.ToggleRXDSP(false); - RadioDriver.ToggleRXDSP(true); + u16 GetScanStep() { return 25_KHz >> (2 >> bwMul); } + u32 GetBW() { return 200_KHz << bwMul; } + u32 GetFStart() { return currentFreq - (100_KHz << bwMul); } + u32 GetFEnd() { return currentFreq + (100_KHz << bwMul); } + + u8 BWMul2XDiv() { return clamp(4 - bwMul, 0, 2); } + u8 GetMeasurementsCount() { + if (bwMul == 3) { + return 64; } + if (bwMul > 3) { + return 128; + } + return 32; + } - DelayUs(scanDelay); - return BK4819Read(0x67); + void ResetRSSI() { + RadioDriver.ToggleRXDSP(false); + RadioDriver.ToggleRXDSP(true); } - u8 GetRssi(u32 f) { - RadioDriver.SetFrequency(f); - return GetRssi(); + u8 GetRssi() { + ResetRSSI(); + + DelayUs(scanDelay); + return (BK4819Read(0x67) & 0x1FF) >> 1; } - inline bool IsFlashLightOn() { return GPIOC->DATA & GPIO_PIN_3; } - inline void TurnOffFlashLight() { + bool IsFlashLightOn() { return GPIOC->DATA & GPIO_PIN_3; } + void TurnOffFlashLight() { GPIOC->DATA &= ~GPIO_PIN_3; gFlashLightStatus = 3; } - inline void ToggleBacklight() { GPIOB->DATA ^= GPIO_PIN_6; } + void ToggleBacklight() { GPIOB->DATA ^= GPIO_PIN_6; } - inline u8 Rssi2Y(u8 rssi) { - return clamp(DrawingEndY - (rssi - rssiMin), 1, DrawingEndY); + u8 Rssi2Y(u8 rssi) { + return DrawingEndY - clamp(rssi - rssiMin, 0, DrawingEndY); } - inline i32 clamp(i32 v, i32 min, i32 max) { - if (v < min) + i32 clamp(i32 v, i32 min, i32 max) { + if (v <= min) return min; - if (v > max) + if (v >= max) return max; return v; } - inline u32 modulo(u32 num, u32 div) { + u32 modulo(u32 num, u32 div) { while (num >= div) num -= div; return num; } TUV_K5Display DisplayBuff; - const TUV_K5SmallNumbers FontSmallNr; CDisplay Display; + const TUV_K5SmallNumbers FontSmallNr; - u8 lastButtonPressed; - u32 currentFreq; - u16 u16OldAfSettings; - - u16 scanDelay; - u8 sampleZoom; - u32 scanStep; u32 frequencyChangeStep; + u8 bwMul; u8 rssiTriggerLevel; - bool stickyPeakTrigger; - bool working = false; - bool bDisplayCleared = true; + u8 btn = 255; + u8 btnPrev = 255; + u32 currentFreq; + u16 oldAFSettings; + u16 oldBWSettings; + + bool isInitialized = false; }; diff --git a/src/spectrum_fagci/types.hpp b/src/spectrum_fagci/types.hpp new file mode 100644 index 0000000..7b6f88c --- /dev/null +++ b/src/spectrum_fagci/types.hpp @@ -0,0 +1,10 @@ +#pragma once + +typedef unsigned char u8; +typedef signed short i16; +typedef unsigned short u16; +typedef signed int i32; +typedef unsigned int u32; +typedef signed long long i64; +typedef unsigned long long u64; +