From 498476fc57e00a9d772d869b5310bef371a7318f Mon Sep 17 00:00:00 2001 From: Sjef Verhoeven PE5PVB Date: Wed, 3 Sep 2025 15:25:51 +0200 Subject: [PATCH] Added RDS statistics analyser --- TEF6686_ESP32.ino | 106 +++++++++++++++++++++----------------- src/TEF6686.cpp | 6 +++ src/TEF6686.h | 2 + src/gui.cpp | 89 ++++++++++++++++++++++++++++++++ src/gui.h | 4 ++ src/rds.cpp | 126 ++++++++++++++++++++++++++++++++++++++++++++-- src/rds.h | 4 ++ 7 files changed, 287 insertions(+), 50 deletions(-) diff --git a/TEF6686_ESP32.ino b/TEF6686_ESP32.ino index 980bd99..91350ce 100644 --- a/TEF6686_ESP32.ino +++ b/TEF6686_ESP32.ino @@ -110,6 +110,7 @@ bool NTPupdated; bool optenc; bool rdsflagreset; bool rdsreset; +bool rdsstatscreen; bool RDSSPYTCP; bool RDSSPYUSB; bool RDSstatus; @@ -427,6 +428,7 @@ unsigned long afticker; unsigned long aftickerhold; unsigned long aftimer; unsigned long autosquelchtimer; +unsigned long blockcounterold[33]; unsigned long eonticker; unsigned long eontickerhold; unsigned long flashingtimer; @@ -437,6 +439,7 @@ unsigned long ModulationpreviousMillis; unsigned long ModulationpeakPreviousMillis; unsigned long NTPtimer; unsigned long peakholdmillis; +unsigned long processed_rdsblocksold[33]; unsigned long pslongticker; unsigned long pslongtickerhold; unsigned long rtplusticker; @@ -1104,7 +1107,7 @@ void loop() { } } - if (!BWtune && !menu && !afscreen && !scandxmode) { + if (!BWtune && !menu && !afscreen && !rdsstatscreen && !scandxmode) { if (af != 0 && dropout && millis() >= aftimer + 1000) { aftimer = millis(); if (radio.af_counter == 0) { @@ -1223,7 +1226,7 @@ void loop() { if (seek) Seek(direction); if ((SStatus / 10 > LowLevelSet) && !LowLevelInit && !BWtune && !menu && band < BAND_GAP) { - if (!screenmute && !advancedRDS && !afscreen) { + if (!screenmute && !advancedRDS && !rdsstatscreen && !afscreen) { if (showmodulation) { tftPrint(-1, "10", 24, 144, ActiveColor, ActiveColorSmooth, 16); tftPrint(-1, "30", 54, 144, ActiveColor, ActiveColorSmooth, 16); @@ -1255,7 +1258,7 @@ void loop() { if ((SStatus / 10 <= LowLevelSet) && band < BAND_GAP) { if (LowLevelInit && !BWtune && !menu) { - if (!screenmute && !afscreen && !advancedRDS) { + if (!screenmute && !rdsstatscreen && !afscreen && !advancedRDS) { for (byte segments = 0; segments < 94; segments++) { if (segments > 54) { if (((segments - 53) % 10) == 0) tft.fillRect(16 + (2 * segments), 141, 2, 2, GreyoutColor); @@ -1315,7 +1318,7 @@ void loop() { doSquelch(); if (millis() >= tuningtimer + 200) readRds(); GetData(); - if (!screenmute && !afscreen && !advancedRDS) ShowModLevel(); + if (!screenmute && !rdsstatscreen && !afscreen && !advancedRDS) ShowModLevel(); } } @@ -1373,7 +1376,7 @@ void loop() { WakeToSleep(REVERSE); while (digitalRead(ROTARY_BUTTON) == LOW); } else { - if (!afscreen) ButtonPress(); + if (!afscreen && !rdsstatscreen) ButtonPress(); } } @@ -1393,7 +1396,7 @@ void loop() { WakeToSleep(REVERSE); while (digitalRead(BWBUTTON) == LOW); } else { - if (!screenmute && !afscreen) BWButtonPress(); + if (!screenmute) BWButtonPress(); } } @@ -1403,7 +1406,7 @@ void loop() { num = GetNum(); if (num != -1) { - if (!screenmute && !BWtune && !menu && !advancedRDS && !afscreen) + if (!screenmute && !BWtune && !menu && !advancedRDS && !rdsstatscreen && !afscreen) { NumpadProcess(num); } @@ -1414,13 +1417,14 @@ void loop() { } void GetData() { - if (!afscreen) ShowSignalLevel(); - if (!BWtune && !menu) showPS(); + if (!afscreen && !rdsstatscreen) ShowSignalLevel(); + if (!BWtune && !menu && !rdsstatscreen) showPS(); if (band < BAND_GAP && !BWtune && !menu) { - if (advancedRDS && !afscreen && !screenmute) ShowAdvancedRDS(); + if (advancedRDS && !afscreen && !rdsstatscreen && !screenmute) ShowAdvancedRDS(); + if (!advancedRDS && !afscreen && rdsstatscreen && !screenmute) ShowRDSStatistics(); if (afscreen && !screenmute) ShowAFEON(); - if (!afscreen) { + if (!afscreen && !rdsstatscreen) { if (!screenmute) ShowErrors(); showPTY(); showECC(); @@ -1870,7 +1874,7 @@ void BANDBUTTONPress() { while (digitalRead(BANDBUTTON) == LOW && counter - counterold <= 1000) counter = millis(); if (counter - counterold < 1000) { - if (afscreen) { + if (afscreen || rdsstatscreen) { leave = true; BuildAdvancedRDS(); freq_in = 0; @@ -2306,7 +2310,7 @@ void ToggleSWMIBand(bool frequencyup) { } void SelectBand() { - if (afscreen || advancedRDS) { + if (afscreen || advancedRDS || rdsstatscreen) { BuildDisplay(); freq_in = 0; } @@ -2429,37 +2433,41 @@ void SelectBand() { void BWButtonPress() { if (seek) radio.setUnMute(); seek = false; - if (scandxmode) { - unsigned long counterold = millis(); - unsigned long counter = millis(); - while (digitalRead(BWBUTTON) == LOW && counter - counterold <= 1000) counter = millis(); - - if (counter - counterold < 1000) { - ShowFreq(5); - ShowFreq(0); - } else { - cancelDXScan(); - } + if (afscreen || rdsstatscreen) { + BuildRDSStatScreen(); } else { - if (!usesquelch) radio.setUnMute(); - if (!BWtune && !menu) { - if (!screenmute) tft.drawBitmap(249, 4, Speaker, 28, 24, GreyoutColor); + if (scandxmode) { unsigned long counterold = millis(); unsigned long counter = millis(); while (digitalRead(BWBUTTON) == LOW && counter - counterold <= 1000) counter = millis(); if (counter - counterold < 1000) { - BuildBWSelector(); - freq_in = 0; - BWtune = true; - BWtemp = BWset; + ShowFreq(5); + ShowFreq(0); } else { - if (band == BAND_FM || band == BAND_OIRT) { - doStereoToggle(); - } else { + cancelDXScan(); + } + } else { + if (!usesquelch) radio.setUnMute(); + if (!BWtune && !menu) { + if (!screenmute) tft.drawBitmap(249, 4, Speaker, 28, 24, GreyoutColor); + unsigned long counterold = millis(); + unsigned long counter = millis(); + while (digitalRead(BWBUTTON) == LOW && counter - counterold <= 1000) counter = millis(); + + if (counter - counterold < 1000) { BuildBWSelector(); freq_in = 0; BWtune = true; + BWtemp = BWset; + } else { + if (band == BAND_FM || band == BAND_OIRT) { + doStereoToggle(); + } else { + BuildBWSelector(); + freq_in = 0; + BWtune = true; + } } } } @@ -2506,6 +2514,9 @@ void ModeButtonPress() { if (afpagenr == 1) afpagenr = 2; else if (afpagenr == 2 && afpage) afpagenr = 3; else afpagenr = 1; BuildAFScreen(); freq_in = 0; + } else if (rdsstatscreen) { + BuildAFScreen(); + freq_in = 0; } else { if (!BWtune && !menu) { if (!screenmute) { @@ -2813,7 +2824,7 @@ void KeyUp() { ShowFreq(5); ShowFreq(0); } else { - if (!afscreen) { + if (!afscreen && !rdsstatscreen) { if (!BWtune && !menu) { switch (tunemode) { case TUNE_MAN: @@ -2881,7 +2892,7 @@ void KeyDown() { ShowFreq(5); ShowFreq(0); } else { - if (!afscreen) { + if (!afscreen && !rdsstatscreen) { if (!BWtune && !menu) { switch (tunemode) { case TUNE_MAN: @@ -3226,7 +3237,7 @@ void ShowFreq(int mode) { } tuningtimer = millis(); - if (!rdsflagreset && !screenmute && !afscreen) { + if (!rdsflagreset && !screenmute && !afscreen && !rdsstatscreen) { ShowRDSLogo(false); if (!advancedRDS) { FullLineSprite.fillSprite(BackgroundColor); @@ -3652,7 +3663,7 @@ void doSquelch() { if (language == LANGUAGE_CHS) SquelchSprite.loadFont(FONT16_CHS); else SquelchSprite.loadFont(FONT16); if (!XDRGTKUSB && !XDRGTKTCP && usesquelch && (!scandxmode || (scandxmode && !scanmute))) { - if (!screenmute && usesquelch && !advancedRDS && !afscreen) { + if (!screenmute && usesquelch && !advancedRDS && !afscreen && !rdsstatscreen) { if (!BWtune && !menu && (Squelch > Squelchold + 2 || Squelch < Squelchold - 2)) { SquelchSprite.setTextColor(PrimaryColor, PrimaryColorSmooth, false); SquelchSprite.fillSprite(BackgroundColor); @@ -3700,7 +3711,7 @@ void doSquelch() { SQ = true; } } - if (!screenmute && usesquelch && !advancedRDS && !afscreen) { + if (!screenmute && usesquelch && !advancedRDS && !afscreen && !rdsstatscreen) { if (Squelch != Squelchold) { SquelchSprite.setTextColor(PrimaryColor, PrimaryColorSmooth, false); SquelchSprite.fillSprite(BackgroundColor); @@ -3756,13 +3767,13 @@ void doSquelch() { void updateBW() {//todo air if (BWset == 0) { - if (!BWtune && !screenmute && !advancedRDS && !afscreen) { + if (!BWtune && !screenmute && !advancedRDS && !afscreen && !rdsstatscreen) { tft.fillRoundRect(248, 36, 69, 18, 2, SecondaryColor); tftPrint(0, "AUTO BW", 282, 38, BackgroundColor, SecondaryColor, 16); } radio.setFMABandw(); } else { - if (!BWtune && !screenmute && !advancedRDS && !afscreen) { + if (!BWtune && !screenmute && !advancedRDS && !afscreen && !rdsstatscreen) { tft.fillRoundRect(248, 36, 69, 18, 2, GreyoutColor); tftPrint(0, "AUTO BW", 282, 38, BackgroundColor, GreyoutColor, 16); } @@ -3772,13 +3783,13 @@ void updateBW() {//todo air void updateiMS() { if (band < BAND_GAP) { if (iMSset == 0) { - if (!screenmute && !advancedRDS && !afscreen && !BWtune) { + if (!screenmute && !advancedRDS && !afscreen && !rdsstatscreen && !BWtune) { tft.fillRoundRect(249, 57, 30, 18, 2, SecondaryColor); tftPrint(0, "iMS", 265, 59, BackgroundColor, SecondaryColor, 16); } radio.setiMS(1); } else { - if (!screenmute && !advancedRDS && !afscreen && !BWtune) { + if (!screenmute && !advancedRDS && !afscreen && !rdsstatscreen && !BWtune) { tft.fillRoundRect(249, 57, 30, 18, 2, GreyoutColor); tftPrint(0, "iMS", 265, 59, BackgroundColor, GreyoutColor, 16); } @@ -3790,13 +3801,13 @@ void updateiMS() { void updateEQ() { if (band < BAND_GAP) { if (EQset == 0) { - if (!screenmute && !advancedRDS && !afscreen && !BWtune) { + if (!screenmute && !advancedRDS && !afscreen && !rdsstatscreen && !BWtune) { tft.fillRoundRect(287, 57, 30, 18, 2, SecondaryColor); tftPrint(0, "EQ", 301, 59, BackgroundColor, SecondaryColor, 16); } radio.setEQ(1); } else { - if (!screenmute && !advancedRDS && !afscreen && !BWtune) { + if (!screenmute && !advancedRDS && !afscreen && !rdsstatscreen && !BWtune) { tft.fillRoundRect(287, 57, 30, 18, 2, GreyoutColor); tftPrint(0, "EQ", 301, 59, BackgroundColor, GreyoutColor, 16); } @@ -4477,6 +4488,9 @@ void MuteScreen(bool setting) { } else if (advancedRDS) { BuildAdvancedRDS(); freq_in = 0; + } else if (rdsstatscreen) { + BuildRDSStatScreen(); + freq_in = 0; } else { BuildDisplay(); freq_in = 0; @@ -4897,7 +4911,7 @@ void startFMDXScan() { } if (menu) endMenu(); - if (afscreen || advancedRDS) { + if (afscreen || advancedRDS || rdsstatscreen) { BuildDisplay(); freq_in = 0; } diff --git a/src/TEF6686.cpp b/src/TEF6686.cpp index f02ce1f..e364e03 100644 --- a/src/TEF6686.cpp +++ b/src/TEF6686.cpp @@ -596,6 +596,9 @@ void TEF6686::readRDS(byte showrdserrors) { } if (!rds.rdsBerror || showrdserrors == 3) rdsblock = rds.rdsB >> 11; else return; + rds.blockcounter[rdsblock]++; + processed_rdsblocks++; + switch (rdsblock) { case RDS_GROUP_0A: case RDS_GROUP_0B: @@ -1783,9 +1786,12 @@ void TEF6686::clearRDS (bool fullsearchrds) { RDSplus2[i] = 0; } + for (i = 0; i < 33; i++) rds.blockcounter[i] = 0; + for (i = 0; i < 10; i++) rds.aid[i] = 0; rdsblock = 254; + processed_rdsblocks = 0; piold = 0; rds.correctPI = 0; rds.ECC = 254; diff --git a/src/TEF6686.h b/src/TEF6686.h index cbad6ed..729079d 100644 --- a/src/TEF6686.h +++ b/src/TEF6686.h @@ -592,6 +592,7 @@ typedef struct _rds_ { char stationState[3]; char dabafeid[5]; char dabafchannel[4]; + unsigned long blockcounter[33]; uint16_t rdsA, rdsB, rdsC, rdsD, rdsErr, rdsStat, correctPI, rdsplusTag1, rdsplusTag2, PICTlock = 0; bool ps12error, ps34error, ps56error, ps78error; time_t time; @@ -732,6 +733,7 @@ class TEF6686 { uint8_t eon_counter; uint8_t logbook_counter; uint8_t rdsblock; + unsigned long processed_rdsblocks; bool mute; bool afmethodB; byte underscore; diff --git a/src/gui.cpp b/src/gui.cpp index f8a1600..869435b 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -478,6 +478,92 @@ void doTheme() { // Use this to put your own colors in: http://www.barth-dev.de } } +void BuildRDSStatScreen() { + // Only build screen if not already active + if (!rdsstatscreen) { + // Set page flags + afscreen = false; + advancedRDS = false; + rdsstatscreen = true; + + // --- Draw frame and static lines --- + tft.fillScreen(BackgroundColor); + tft.drawRect(0, 0, 320, 240, FrameColor); + tft.drawLine(0, 30, 320, 30, FrameColor); + tft.drawLine(0, 218, 320, 218, FrameColor); + tft.drawLine(30, 30, 30, 0, FrameColor); + tft.drawLine(66, 30, 66, 0, FrameColor); + tft.drawLine(105, 30, 105, 0, FrameColor); + tft.drawLine(162, 30, 162, 0, FrameColor); + tft.drawLine(248, 30, 248, 0, FrameColor); + tft.drawLine(0, 50, 320, 50, FrameColor); + tft.drawLine(208, 30, 208, 50, FrameColor); + tft.drawLine(80, 30, 80, 218, FrameColor); + tft.drawLine(160, 30, 160, 218, FrameColor); + tft.drawLine(240, 30, 240, 218, FrameColor); + + // --- Column headers --- + tftPrint(-1, "kHz", 205, 4, ActiveColor, ActiveColorSmooth, 28); + + // --- Labels above packet columns --- + tftPrint(-1, "ERRORS", 3, 34, ActiveColor, ActiveColorSmooth, 16); + tftPrint(-1, "A:", 66, 34, ActiveColor, ActiveColorSmooth, 16); + tftPrint(-1, "B:", 104, 34, ActiveColor, ActiveColorSmooth, 16); + tftPrint(-1, "C:", 142, 34, ActiveColor, ActiveColorSmooth, 16); + tftPrint(-1, "D:", 180, 34, ActiveColor, ActiveColorSmooth, 16); + tftPrint(-1, "PACKETS", 210, 34, ActiveColor, ActiveColorSmooth, 16); + + // --- Group labels setup --- + const uint16_t xcol[4] = {10, 90, 170, 250}; // column X positions + const uint16_t rowY[8] = {56, 76, 96, 116, 136, 156, 176, 196}; // row Y positions + + // Labels for each group (A/B) + const char* const groups[16][2] = { + {"0A", "0B"}, {"1A", "1B"}, {"2A", "2B"}, {"3A", "3B"}, + {"4A", "4B"}, {"5A", "5B"}, {"6A", "6B"}, {"7A", "7B"}, + {"8A", "8B"}, {"9A", "9B"}, {"10A", "10B"}, {"11A", "11B"}, + {"12A", "12B"}, {"13A", "13B"}, {"14A", "14B"}, {"15A", "15B"} + }; + + // --- Draw group labels in columns with correct Y positions --- + for (uint8_t col = 0; col < 4; col++) { // 4 columns + for (uint8_t row = 0; row < 4; row++) { // 4 groups per column + uint8_t g = col * 4 + row; // group index 0..15 + tftPrint(-1, groups[g][0], xcol[col], rowY[row * 2], ActiveColor, ActiveColorSmooth, 16); // A + tftPrint(-1, groups[g][1], xcol[col], rowY[row * 2 + 1], ActiveColor, ActiveColorSmooth, 16); // B + } + } + + // --- Draw 0.0 % in four columns --- + const uint16_t pctX[4] = {70, 150, 230, 310}; + for (uint8_t c = 0; c < 4; c++) { + for (uint8_t y = 56; y < 216; y += 20) { + tftPrint(1, "0.0", pctX[c] - 10, y, GreyoutColor, BackgroundColor, 16); // placeholder value + tftPrint(0, "%", pctX[c], y, GreyoutColor, BackgroundColor, 16); + } + } + + // --- Reset stored states --- + RDSstatusold = !RDSstatusold; + Stereostatusold = false; + BWreset = true; + rssiold = 2000; + batteryold = 6; + batteryVold = 0; + vPerold = 0; + rds_clockold = ""; + dropout = false; + rdsreset = true; + + // --- Clear previous RDS block counters --- + for (int x = 0; x < 33; x++) { + processed_rdsblocksold[x] = 0; + blockcounterold[x] = 0; + } + } +} + + void BuildAFScreen() { if (!afscreen && RDSstatus) { tft.drawRoundRect(20, 30, 274, 170, 5, ActiveColor); @@ -487,6 +573,7 @@ void BuildAFScreen() { } afscreen = true; advancedRDS = false; + rdsstatscreen = false; tft.fillScreen(BackgroundColor); tft.drawRect(0, 0, 320, 240, FrameColor); @@ -2850,6 +2937,7 @@ void BuildMenu() { void BuildAdvancedRDS() { afscreen = false; + rdsstatscreen = false; afpage = false; afpagenr = 1; advancedRDS = true; @@ -2980,6 +3068,7 @@ void BuildAdvancedRDS() { void BuildDisplay() { afscreen = false; + rdsstatscreen = false; advancedRDS = false; BWtune = false; diff --git a/src/gui.h b/src/gui.h index ad92105..ea2adaf 100644 --- a/src/gui.h +++ b/src/gui.h @@ -41,6 +41,7 @@ extern bool mwstepsize; extern bool rdsreset; extern bool RDSstatus; extern bool RDSstatusold; +extern bool rdsstatscreen; extern bool rdsstereoold; extern bool usesquelch; extern bool scandxmode; @@ -205,6 +206,8 @@ extern unsigned int mappedfreqold2[20]; extern unsigned int mappedfreqold3[20]; extern unsigned int memstartfreq; extern unsigned int memstopfreq; +extern unsigned long blockcounterold[33]; +extern unsigned long processed_rdsblocksold[33]; extern unsigned long scantimer; extern byte items[10]; extern const size_t language_totalnumber; @@ -219,6 +222,7 @@ extern TFT_eSprite PSSprite; extern TFT_eSprite FrequencySprite; void BuildAFScreen(); +void BuildRDSStatScreen(); void BuildMenu(); void BuildAdvancedRDS(); void BuildDisplay(); diff --git a/src/rds.cpp b/src/rds.cpp index 4e99b1b..84e55dc 100644 --- a/src/rds.cpp +++ b/src/rds.cpp @@ -4,6 +4,8 @@ #include "constants.h" #include +String HexStringold; + int RadiotextWidth, PSLongWidth, AIDWidth, afstringWidth, eonstringWidth, rtplusstringWidth, lengths[7]; String afstringold, eonstringold, rtplusstringold, stationNameLongOld, AIDStringold; bool rtABold, ps12errorold, ps34errorold, ps56errorold, ps78errorold; @@ -307,7 +309,7 @@ void readRds() { RDSstatus = radio.rds.hasRDS; ShowRDSLogo(RDSstatus); - if (!screenmute && !afscreen) { + if (!screenmute && !afscreen && !rdsstatscreen) { if (!RDSstatus) { if (radio.rds.correctPI != 0 && !dropout) { if (radio.rds.region == 0) { @@ -553,7 +555,7 @@ void ShowErrors() { void showPI() { if ((radio.rds.region != 0 && (String(radio.rds.picode) != PIold || radio.rds.stationIDtext != stationIDold || radio.rds.stationStatetext != stationStateold)) || (radio.rds.region == 0 && String(radio.rds.picode) != PIold)) { - if (!afscreen && !radio.rds.rdsAerror && !radio.rds.rdsBerror && !radio.rds.rdsCerror && !radio.rds.rdsDerror && radio.rds.rdsA != radio.rds.correctPI && PIold.length() > 1) { + if (!rdsstatscreen && !afscreen && !radio.rds.rdsAerror && !radio.rds.rdsBerror && !radio.rds.rdsCerror && !radio.rds.rdsDerror && radio.rds.rdsA != radio.rds.correctPI && PIold.length() > 1) { radio.clearRDS(fullsearchrds); if (RDSSPYUSB) Serial.print("G:\r\nRESET-------\r\n\r\n"); if (RDSSPYTCP) RemoteClient.print("G:\r\nRESET-------\r\n\r\n"); @@ -579,7 +581,7 @@ void showPI() { } } else if (afscreen) { tftReplace(-1, PIold, radio.rds.picode, 30, 201, BWAutoColor, BWAutoColorSmooth, BackgroundColor, 16); - } else { + } else if (!rdsstatscreen) { if (radio.rds.region == 0) { if (!RDSstatus) { tftReplace(0, PIold, radio.rds.picode, 275, 187, RDSDropoutColor, RDSDropoutColorSmooth, BackgroundColor, 28); @@ -661,7 +663,7 @@ void showPS() { if (!screenmute) { tftReplace(0, PSold, radio.rds.stationName, 160, 201, BWAutoColor, BWAutoColorSmooth, BackgroundColor, 16); } - } else { + } else if (!rdsstatscreen) { // Handle long PS display if (radio.rds.hasLongPS && showlongps) { String stationNameLongString = String(radio.rds.stationNameLong) + " "; // Add trailing spaces for scrolling @@ -1230,4 +1232,120 @@ void ShowAFEON() { } } +void ShowRDSStatistics() { + // Only update if RDS is active, blocks processed, and no errors in Block A-D + if (RDSstatus && radio.processed_rdsblocks > 0 && + !radio.rds.rdsAerror && !radio.rds.rdsBerror && + !radio.rds.rdsCerror && !radio.rds.rdsDerror) { + + // --- Draw A-D error circles --- + const uint8_t xErr[4] = {86, 124, 162, 200}; // X positions for A-D + const bool errors[4] = {radio.rds.rdsAerror, radio.rds.rdsBerror, + radio.rds.rdsCerror, radio.rds.rdsDerror + }; + + for (uint8_t i = 0; i < 4; i++) { + tft.fillCircle(xErr[i], 41, 5, errors[i] ? SignificantColor : InsignificantColor); + } + + // --- Update total processed RDS blocks if changed --- + if (processed_rdsblocksold[32] != radio.processed_rdsblocks) { + tftReplace(1, String(processed_rdsblocksold[32]), String(radio.processed_rdsblocks), + 318, 34, PrimaryColor, PrimaryColorSmooth, BackgroundColor, 16); + processed_rdsblocksold[32] = radio.processed_rdsblocks; + } + + // --- Row Y positions (repeats every 8 groups) --- + const uint8_t rdsYpos[] PROGMEM = {56, 76, 96, 116, 136, 156, 176, 196}; + + uint8_t rb = radio.rdsblock; // current RDS block + + // --- Determine column X positions based on group range --- + uint16_t xpos, xposPct; + if (rb <= RDS_GROUP_3B ) { + xpos = 60; + xposPct = 70; + } + else if (rb <= RDS_GROUP_7B ) { + xpos = 140; + xposPct = 150; + } + else if (rb <= RDS_GROUP_11B) { + xpos = 220; + xposPct = 230; + } + else { + xpos = 300; + xposPct = 310; + } + + // --- Determine row Y position (wraps every 8 groups) --- + uint8_t row = rb & 0x07; // modulo 8 + uint8_t ypos = pgm_read_byte(&rdsYpos[row]); // Y position + + // --- Persist last drawn dot between calls --- + static int16_t lastX = -1, lastY = -1; // -1 = "none yet" + + // Only update if the block counter has changed + if (blockcounterold[rb] != radio.rds.blockcounter[rb]) { + // --- Calculate old and new percentage --- + float oldPerc = (blockcounterold[rb] * 100.0f) / processed_rdsblocksold[rb]; + float newPerc = (radio.rds.blockcounter[rb] * 100.0f) / radio.processed_rdsblocks; + + char oldBuf[6], newBuf[6]; // buffers for dtostrf + dtostrf(oldPerc, 0, 1, oldBuf); + dtostrf(newPerc, 0, 1, newBuf); + + // --- Draw previous position as "Significant" if it exists and is different --- + if (lastX >= 0 && (lastX != xpos || lastY != ypos)) { + tft.fillCircle(lastX - 55, lastY + 7, 2, SignificantColor); + } + + // --- Update percentage display --- + tftReplace(1, oldBuf, newBuf, xpos, ypos, PrimaryColor, PrimaryColorSmooth, BackgroundColor, 16); + tftPrint(0, "%", xposPct, ypos, ActiveColor, ActiveColorSmooth, 16); + + // --- Draw current dot as "Insignificant" --- + tft.fillCircle(xpos - 55, ypos + 7, 2, InsignificantColor); + + // --- Save current as last for next update --- + lastX = xpos; + lastY = ypos; + + // --- Store updated block counters --- + blockcounterold[rb] = radio.rds.blockcounter[rb]; + processed_rdsblocksold[rb] = radio.processed_rdsblocks; + } + + String HexString; + // Convert 16-bit blocks rdsA..rdsD into 4-digit HEX strings + HexString = String(((radio.rds.rdsA >> 12) & 0xF), HEX) + + String(((radio.rds.rdsA >> 8) & 0xF), HEX) + + String(((radio.rds.rdsA >> 4) & 0xF), HEX) + + String(( radio.rds.rdsA & 0xF), HEX) + " "; + + HexString += String(((radio.rds.rdsB >> 12) & 0xF), HEX) + + String(((radio.rds.rdsB >> 8) & 0xF), HEX) + + String(((radio.rds.rdsB >> 4) & 0xF), HEX) + + String(( radio.rds.rdsB & 0xF), HEX) + " "; + + HexString += String(((radio.rds.rdsC >> 12) & 0xF), HEX) + + String(((radio.rds.rdsC >> 8) & 0xF), HEX) + + String(((radio.rds.rdsC >> 4) & 0xF), HEX) + + String(( radio.rds.rdsC & 0xF), HEX) + " "; + + HexString += String(((radio.rds.rdsD >> 12) & 0xF), HEX) + + String(((radio.rds.rdsD >> 8) & 0xF), HEX) + + String(((radio.rds.rdsD >> 4) & 0xF), HEX) + + String(( radio.rds.rdsD & 0xF), HEX); + + // Make uppercase + HexString.toUpperCase(); + + if (HexString != HexStringold) { + tftReplace(0, HexStringold, HexString, 160, 222, ActiveColor, ActiveColorSmooth, BackgroundColor, 16); + HexStringold = HexString; + } + } +} #pragma GCC diagnostic pop \ No newline at end of file diff --git a/src/rds.h b/src/rds.h index 29e06c6..2b02ed6 100644 --- a/src/rds.h +++ b/src/rds.h @@ -33,6 +33,7 @@ extern bool rdsreset; extern bool RDSSPYTCP; extern bool RDSSPYUSB; extern bool RDSstatus; +extern bool rdsstatscreen; extern bool rdsstereoold; extern bool rtcset; extern bool screenmute; @@ -123,8 +124,10 @@ extern unsigned int mappedfreqold3[20]; extern unsigned long afticker; extern unsigned long aftickerhold; extern unsigned long aftimer; +extern unsigned long blockcounterold[33]; extern unsigned long eonticker; extern unsigned long eontickerhold; +extern unsigned long processed_rdsblocksold[33]; extern unsigned long pslongticker; extern unsigned long pslongtickerhold; extern unsigned long rtplusticker; @@ -152,6 +155,7 @@ void showRadioText(); void ShowAFEON(); void showCT(); void ShowErrors(); +void ShowRDSStatistics(); extern void ShowRDSLogo(bool RDSstatus); extern void DataPrint(String string);