From 0b913edc7d707c1d675e64a7456dfd7c569807e3 Mon Sep 17 00:00:00 2001 From: MCelliotG Date: Mon, 27 May 2024 04:48:19 +0300 Subject: [PATCH 01/13] Greek PTY labels --- src/language.h | 64 +++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/language.h b/src/language.h index 5ffc846..adf1856 100644 --- a/src/language.h +++ b/src/language.h @@ -1282,38 +1282,38 @@ static const char* const myLanguage[18][260] PROGMEM = { "Σβήσιμο", // 225 "Σβήσιμο οθόνης", // 226 "Ανενεργό", // 227 - "None", // 228 - "News", // 229 - "Current Affairs", // 230 - "Information", // 231 - "Sport", // 232 - "Education", // 233 - "Drama", // 234 - "Culture", // 235 - "Science", // 236 - "Varied", // 237 - "Pop Music", // 238 - "Rock Music", // 239 - "Easy Listening", // 240 - "Light Classical", // 241 - "Serious Classical", // 242 - "Other Music", // 243 - "Weather", // 244 - "Finance", // 245 - "Children's Progs", // 246 - "Social Affairs", // 247 - "Religion", // 248 - "Phone-In", // 249 - "Travel", // 250 - "Leisure", // 251 - "Jazz Music", // 252 - "Country Music", // 253 - "National Music", // 254 - "Oldies Music", // 255 - "Folk Music", // 256 - "Documentary", // 257 - "Alarm Test", // 258 - "Alarm!!!" // 259 + "Κανένα", // 228 + "Ειδήσεις", // 229 + "Επικαιρότητα", // 230 + "Πληροφόρηση", // 231 + "Σπορ", // 232 + "Εκπαίδευση", // 233 + "Δράμα", // 234 + "Πολιτισμός", // 235 + "Επιστήμη", // 236 + "Ποικίλο", // 237 + "Ποπ μουσική", // 238 + "Ροκ μουσική", // 239 + "Εύκολη ακρόαση", // 240 + "Ελαφρά κλασική", // 241 + "Σοβαρή κλασική", // 242 + "Άλλη μουσική", // 243 + "Καιρός", // 244 + "Οικονομικά", // 245 + "Παιδικό πρόγραμμα", // 246 + "Κοινωνικά", // 247 + "Θρησκεία", // 248 + "Τηλεφωνικά", // 249 + "Ταξίδια", // 250 + "Ελεύθερος χρόνος", // 251 + "Τζαζ μουσική", // 252 + "Κάντρι μουσική", // 253 + "Εθνική μουσική", // 254 + "Παλαιές επιτυχίες", // 255 + "Παραδοσιακή μουσική", // 256 + "Ντοκιμαντέρ", // 257 + "Δοκιμή συναγερμού", // 258 + "Συναγερμός!!!" // 259 }, { "Română", // Romanian From 7a0859556a00d89d1f1fe0b93ec2c0d07476406e Mon Sep 17 00:00:00 2001 From: DXHR05 <135708288+DXHR05@users.noreply.github.com> Date: Mon, 27 May 2024 09:57:32 +0200 Subject: [PATCH 02/13] Update language.h Updated Croatian --- src/language.h | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/language.h b/src/language.h index 5ffc846..8aa79ea 100644 --- a/src/language.h +++ b/src/language.h @@ -988,7 +988,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "FM", // 193 "AM", // 194 "SPOJI", // 195 - "DX MODE", // 196 + "DX NAČIN RADA", // 196 "Stationlist ID", // 197 "AM ATT", // 198 "FM deemphasis", // 199 @@ -998,28 +998,28 @@ static const char* const myLanguage[18][260] PROGMEM = { "Brzi PS", // 203 "Zadano", // 204 "Isključi zaslon\ntijekom veze XDRGTK-om", // 205 - "FMSI stereo\nimprovement", // 206 - "Function only available\non TEF6687/6689!", // 207 - "Function not available\non selected skin!", // 208 - "Begin scanning\nmemory channels", // 209 - "Stop scanning\nmemory channels", // 210 - "Wait time", // 211 - "Start DX scanning", // 212 - "FM DX Options", // 213 - "Abort! The start channel\nis marked as skipped.", // 214 + "Poboljšanje\nFMSI stereo-a", // 206 + "Funkcija dostupna samo\nna TEF6687/6689!", // 207 + "Funkcija nije dostupna\na odabranoj temi dizajna!", // 208 + "Pokreni skeniranje\nmemorijskih kanala", // 209 + "Zaustavi skeniranje\nmemorijskih kanala", // 210 + "Vrijeme čekanja", // 211 + "Pokreni DX skeniranje", // 212 + "FM DX opcije", // 213 + "Prekid! Početni kanal je\noznačen kao preskočen.", // 214 "Autom. sortiranje AF-a", // 215 - "Memory channels", // 216 - "Exclude", // 217 - "Only", // 218 - "Auto cancel scan", // 219 - "Correct PI", // 220 + "Memorijski kanali", // 216 + "Isključeno", // 217 + "Samo", // 218 + "Autom. otkazivanje skeniranja", // 219 + "Točan PI", // 220 "Signal", // 221 - "Mute audio\nwhile scanning", // 222 + "Isključi zvuk\ntijekom skeniranja", // 222 "AIR", // 223 - "5 sec. bandbutton press", // 224 - "Power off", // 225 - "Mute screen", // 226 - "Disabled", // 227 + "Pritisnite tipku band 5 sekundi", // 224 + "Isključivanje", // 225 + "Isključi zaslon", // 226 + "Onemogućeno", // 227 "Neodređeno", // 228 "Vijesti", // 229 "Aktualno", // 230 From 6935955fa7f01ba23d3d36089af5324f58f1036c Mon Sep 17 00:00:00 2001 From: Sjef Verhoeven PE5PVB Date: Mon, 27 May 2024 16:06:58 +0200 Subject: [PATCH 03/13] Undo bootsequence change --- TEF6686_ESP32.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TEF6686_ESP32.ino b/TEF6686_ESP32.ino index 9d74658..0887f8e 100644 --- a/TEF6686_ESP32.ino +++ b/TEF6686_ESP32.ino @@ -794,8 +794,8 @@ void setup() { Wire.endTransmission(); } - if (tunemode == TUNE_MEM) DoMemoryPosTune(); SelectBand(); + if (tunemode == TUNE_MEM) DoMemoryPosTune(); setupmode = false; From 0d5ab0f9bef6f72dc6e9fe2133303a9446b3099c Mon Sep 17 00:00:00 2001 From: Sjef Verhoeven PE5PVB Date: Mon, 27 May 2024 17:00:33 +0200 Subject: [PATCH 04/13] Fixed wrong MEM colors --- TEF6686_ESP32.ino | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/TEF6686_ESP32.ino b/TEF6686_ESP32.ino index 0887f8e..91a8758 100644 --- a/TEF6686_ESP32.ino +++ b/TEF6686_ESP32.ino @@ -2556,8 +2556,7 @@ void KeyUp() { if (!memorystore) { DoMemoryPosTune(); } else { - if (!IsStationEmpty()) memoryposstatus = MEM_EXIST; - else memoryposstatus = MEM_NORMAL; + if (!IsStationEmpty()) memoryposstatus = MEM_EXIST; else memoryposstatus = MEM_DARK; } ShowMemoryPos(); EEPROM.writeByte(EE_BYTE_MEMORYPOS, memorypos); @@ -2623,8 +2622,7 @@ void KeyDown() { if (!memorystore) { DoMemoryPosTune(); } else { - if (!IsStationEmpty()) memoryposstatus = MEM_EXIST; - else memoryposstatus = MEM_NORMAL; + if (!IsStationEmpty()) memoryposstatus = MEM_EXIST; else memoryposstatus = MEM_DARK; } ShowMemoryPos(); EEPROM.writeByte(EE_BYTE_MEMORYPOS, memorypos); @@ -2678,6 +2676,11 @@ void ShowMemoryPos() { if (tunemode == TUNE_MEM) { int memposcolor = 0; int memposcolorsmooth = 0; + + if (!memorystore) { + if (IsStationEmpty()) memoryposstatus = MEM_DARK; else memoryposstatus = MEM_NORMAL; + } + switch (memoryposstatus) { case MEM_DARK: memposcolor = InsignificantColor; From 9b9ecc03a964885465a64c6e0e498862edf923ea Mon Sep 17 00:00:00 2001 From: Mateusz Walkowiak <52051360+HyperDX@users.noreply.github.com> Date: Mon, 27 May 2024 18:48:47 +0200 Subject: [PATCH 05/13] Update language.h --- src/language.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/language.h b/src/language.h index 5ba8320..4c41a9c 100644 --- a/src/language.h +++ b/src/language.h @@ -780,7 +780,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "Wyd. Społeczne", // 247 "Religia", // 248 "Telefon.Udział.", // 249 - "Podróze", // 250 + "Podróże", // 250 "Czas Wolny", // 251 "Muzyka Jazz", // 252 "Muzyka Country", // 253 From 0c4d48c4daa42888474748d446837cc9ddac5d0b Mon Sep 17 00:00:00 2001 From: Sjef Verhoeven PE5PVB Date: Mon, 27 May 2024 19:30:39 +0200 Subject: [PATCH 06/13] Fixed regression on PTYN --- src/TEF6686.cpp | 2 ++ src/TEF6686.h | 3 ++- src/rds.cpp | 4 ++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/TEF6686.cpp b/src/TEF6686.cpp index 6d2e425..0e00c9d 100644 --- a/src/TEF6686.cpp +++ b/src/TEF6686.cpp @@ -1372,6 +1372,7 @@ void TEF6686::readRDS(byte showrdserrors) { RDScharConverter(ptyn_buffer, PTYNtext, sizeof(PTYNtext) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII String utf8String = convertToUTF8(PTYNtext); // Convert RDS characterset to ASCII rds.PTYN = extractUTF8Substring(utf8String, 0, 8, false); // Make sure text is not longer than 8 chars + rds.hasPTYN = true; } } } break; @@ -1761,6 +1762,7 @@ void TEF6686::clearRDS (bool fullsearchrds) { rds.hasCT = false; rds.hasTMC = false; rds.hasAID = false; + rds.hasPTYN = false; rds.hasLongPS = false; rds.hasRDSplus = false; rds.hasDABAF = false; diff --git a/src/TEF6686.h b/src/TEF6686.h index dc6862a..c99d8ae 100644 --- a/src/TEF6686.h +++ b/src/TEF6686.h @@ -611,6 +611,7 @@ typedef struct _rds_ { bool hasTMC; bool hasAF; bool hasCT; + bool hasPTYN; bool rtAB; bool rtAB32; bool hasRDSplus; @@ -707,7 +708,7 @@ class TEF6686 { void setUnMute(); void setVolume(int8_t volume); void tone(uint16_t time, int16_t amplitude, uint16_t frequency); - String trimTrailingSpaces(String str); + String trimTrailingSpaces(String str); uint8_t af_counter; uint8_t eon_counter; uint8_t logbook_counter; diff --git a/src/rds.cpp b/src/rds.cpp index 4c5c55c..8041362 100644 --- a/src/rds.cpp +++ b/src/rds.cpp @@ -48,11 +48,11 @@ void ShowAdvancedRDS() { if (ptynold != radio.rds.PTYN || rdsreset) { if (!screenmute) { - if (ptynold != "PTYN N/A") { + if (ptynold != "PTYN N/A" || radio.rds.hasPTYN) { tftPrint(-1, "PTYN N/A", 216, 109, BackgroundColor, BackgroundColor, 16); tftPrint(-1, ptynold, 216, 109, BackgroundColor, BackgroundColor, 16); } - if (radio.rds.PTYN.length() == 0) radio.rds.PTYN = "PTYN N/A"; + if (!radio.rds.hasPTYN) radio.rds.PTYN = "PTYN N/A"; tftPrint(-1, String(radio.rds.PTYN), 216, 109, RDSColor, RDSColorSmooth, 16); ptynold = radio.rds.PTYN; } From 924eed42501449877b1bb140a1271d81374a2933 Mon Sep 17 00:00:00 2001 From: Sjef Verhoeven PE5PVB Date: Mon, 27 May 2024 20:24:08 +0200 Subject: [PATCH 07/13] Added extra Fast PS setting (always) --- src/TEF6686.cpp | 8 +- src/TEF6686.h | 2 +- src/gui.cpp | 34 +- src/language.h | 1082 ++++++++++++++++++++++++----------------------- 4 files changed, 592 insertions(+), 534 deletions(-) diff --git a/src/TEF6686.cpp b/src/TEF6686.cpp index 0e00c9d..a66801f 100644 --- a/src/TEF6686.cpp +++ b/src/TEF6686.cpp @@ -606,7 +606,7 @@ void TEF6686::readRDS(byte showrdserrors) { ps_buffer[(offset * 2) + 1] = rds.rdsD & 0xFF; // Second character of segment ps_buffer[8] = '\0'; // Endmarker - if (ps_process || !rds.fastps) { + if (ps_process || rds.fastps == 0) { if (offset == 0) { packet0 = true; packet1 = false; @@ -618,7 +618,7 @@ void TEF6686::readRDS(byte showrdserrors) { if (offset == 3) packet3 = true; } - if (packet0 && packet1 && packet2 && packet3 && (ps_process || !rds.fastps)) { // Last chars are received + if (packet0 && packet1 && packet2 && packet3 && (ps_process || rds.fastps == 0)) { // Last chars are received if (strcmp(ps_buffer, ps_buffer2) == 0) { // When no difference between current and buffer, let's go... ps_process = true; RDScharConverter(ps_buffer2, PStext, sizeof(PStext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII @@ -631,7 +631,7 @@ void TEF6686::readRDS(byte showrdserrors) { } } - if (!ps_process && rds.fastps) { // Let's get 2 runs of 8 PS characters fast and without refresh + if (!ps_process && rds.fastps > 0) { // Let's get 2 runs of 8 PS characters fast and without refresh if (offset == 0) packet0 = true; if (offset == 1) packet1 = true; if (offset == 2) packet2 = true; @@ -639,7 +639,7 @@ void TEF6686::readRDS(byte showrdserrors) { RDScharConverter(ps_buffer, PStext, sizeof(PStext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII String utf8String = convertToUTF8(PStext); // Convert RDS characterset to ASCII rds.stationName = extractUTF8Substring(utf8String, 0, 8, true); - if (packet0 && packet1 && packet2 && packet3) ps_process = true; // OK, we had one runs, now let's go the idle PS writing + if (packet0 && packet1 && packet2 && packet3 && rds.fastps < 2) ps_process = true;// OK, we had one runs, now let's go the idle PS writing } if (offset == 0) rds.hasDynamicPTY = bitRead(rds.rdsB, 2) & 0x1F; // Dynamic PTY flag diff --git a/src/TEF6686.h b/src/TEF6686.h index c99d8ae..81ff382 100644 --- a/src/TEF6686.h +++ b/src/TEF6686.h @@ -582,6 +582,7 @@ typedef struct _rds_ { uint16_t aid[10]; uint32_t dabaffreq; byte aid_counter; + byte fastps; int8_t offset; unsigned int ECC; unsigned int LIC; @@ -619,7 +620,6 @@ typedef struct _rds_ { bool rdsreset; bool pierrors; bool sortaf; - bool fastps; bool rtbuffer = true; bool afreg; RdsPiBuffer piBuffer; diff --git a/src/gui.cpp b/src/gui.cpp index a3cc53d..1d90d25 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -1327,7 +1327,12 @@ void ShowOneLine(byte position, byte item, bool selected) { FullLineSprite.drawString(removeNewline(myLanguage[language][203]), 6, 3); FullLineSprite.setTextDatum(TR_DATUM); FullLineSprite.setTextColor(PrimaryColor, PrimaryColorSmooth, false); - FullLineSprite.drawString((radio.rds.fastps ? myLanguage[language][42] : myLanguage[language][30]), 298, 3); + + switch (radio.rds.fastps) { + case 0: FullLineSprite.drawString(myLanguage[language][30], 298, 3); break; + case 1: FullLineSprite.drawString(myLanguage[language][260], 298, 3); break; + case 2: FullLineSprite.drawString(myLanguage[language][261], 298, 3); break; + } break; case FMSETTINGS: @@ -2212,9 +2217,15 @@ void MenuUp() { break; case ITEM9: - radio.rds.fastps = !radio.rds.fastps; + radio.rds.fastps++; + if (radio.rds.fastps > 2) radio.rds.fastps = 0; + + switch (radio.rds.fastps) { + case 0: OneBigLineSprite.drawString(myLanguage[language][30], 135, 0); break; + case 1: OneBigLineSprite.drawString(myLanguage[language][260], 135, 0); break; + case 2: OneBigLineSprite.drawString(myLanguage[language][261], 135, 0); break; + } - OneBigLineSprite.drawString((radio.rds.fastps ? myLanguage[language][42] : myLanguage[language][30]), 135, 0); OneBigLineSprite.pushSprite(24, 118); break; } @@ -3061,9 +3072,15 @@ void MenuDown() { break; case ITEM9: - radio.rds.fastps = !radio.rds.fastps; + radio.rds.fastps--; + if (radio.rds.fastps > 2) radio.rds.fastps = 2; + + switch (radio.rds.fastps) { + case 0: OneBigLineSprite.drawString(myLanguage[language][30], 135, 0); break; + case 1: OneBigLineSprite.drawString(myLanguage[language][260], 135, 0); break; + case 2: OneBigLineSprite.drawString(myLanguage[language][261], 135, 0); break; + } - OneBigLineSprite.drawString((radio.rds.fastps ? myLanguage[language][42] : myLanguage[language][30]), 135, 0); OneBigLineSprite.pushSprite(24, 118); break; } @@ -3921,7 +3938,12 @@ void DoMenu() { case ITEM9: Infoboxprint(myLanguage[language][203]); - OneBigLineSprite.drawString((radio.rds.fastps ? myLanguage[language][42] : myLanguage[language][30]), 135, 0); + switch (radio.rds.fastps) { + case 0: OneBigLineSprite.drawString(myLanguage[language][30], 135, 0); break; + case 1: OneBigLineSprite.drawString(myLanguage[language][260], 135, 0); break; + case 2: OneBigLineSprite.drawString(myLanguage[language][261], 135, 0); break; + } + OneBigLineSprite.pushSprite(24, 118); break; } diff --git a/src/language.h b/src/language.h index 4c41a9c..da7dd40 100644 --- a/src/language.h +++ b/src/language.h @@ -4,8 +4,8 @@ #define VERSION "v2.10 RC8" // [number of languages][number of texts] -// *** means the text is the same as in English -static const char* const myLanguage[18][260] PROGMEM = { + +static const char* const myLanguage[18][262] PROGMEM = { { "English", // English "Rotary direction changed", // 1 "Please release button", // 2 @@ -265,7 +265,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Folk Music", // 256 "Documentary", // 257 "Alarm Test", // 258 - "Alarm!!!" // 259 + "Alarm!!!", // 259 + "Initial", // 260 + "Always" //261 }, { "Nederlands", // Dutch @@ -278,8 +280,8 @@ static const char* const myLanguage[18][260] PROGMEM = { "encoder ingesteld als standaard", // 7 "FM/AM ontvanger", // 8 "Tuner: !Geen!", // 9 - "Volume", // 10 *** - "Converter", // 11 *** + "Volume", // 10 + "Converter", // 11 "Lage bandgrens", // 12 "Hoge bandgrens", // 13 "FM RF niveau afwijking", // 14 @@ -311,14 +313,14 @@ static const char* const myLanguage[18][260] PROGMEM = { "Kies taal", // 40 "DRUK OP MODE OM AF TE SLUITEN", // 41 "Aan", // 42 - "Softmute FM", // 43 *** - "Softmute AM", // 44 *** + "Softmute FM", // 43 + "Softmute AM", // 44 "Piep op bandeinde", // 45 "Regio", // 46 "Europa", // 47 - "Amerika", // 48 *** + "Amerika", // 48 "Toon underscore in RDS", // 49 - "USB mode", // 50 *** + "USB mode", // 50 "Wi-Fi aktief", // 51 "Configureer Wi-Fi", // 52 "Verbind met: ", // 53 @@ -326,9 +328,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Verbinden met Wi-Fi...", // 55 "MISLUKT!", // 56 "VERBONDEN!", // 57 - "Stationlist client IP", // 58 *** + "Stationlist client IP", // 58 "Toon SW golflengte", // 59 - "RDS filter", // 60 *** + "RDS filter", // 60 "Toon PI fouten", // 61 "Gebruik squelch", // 62 "Modulatie meter", // 63 @@ -346,9 +348,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Auto uitschakelen", // 75 "Scherm uit", // 76 "Thema", // 77 - "Uiterlijk", // 78 *** + "Uiterlijk", // 78 "Gedetecteerde applicaties", // 79 - "Min.", // 80 *** + "Min.", // 80 "Dynamische\nSPI snelheid", // 81 "Zoek gevoeligheid", // 82 "Geen", // 83 @@ -370,65 +372,65 @@ static const char* const myLanguage[18][260] PROGMEM = { "Gebruik AF", // 99 "Selecteer\nFM banden", // 100 "Selecteer\nAM banden", // 101 - "LW", // 102 *** - "MW", // 103 *** - "SW", // 104 *** - "FM", // 105 *** - "OIRT", // 106 *** + "LW", // 102 + "MW", // 103 + "SW", // 104 + "FM", // 105 + "OIRT", // 106 "Afstemmen tijdens\nschermbeveiliging", // 107 "Model keuze", // 108 "Basis (ILI9341)", // 109 - "Portable (ILI9341)", // 110 *** - "Portable touch (ILI9341)", // 111 *** + "Portable (ILI9341)", // 110 + "Portable touch (ILI9341)", // 111 "Titel", // 112 - "Album", // 113 *** + "Album", // 113 "Nummer", // 114 "Artiest", // 115 "Compositie", // 116 "Deel", // 117 "Dirigent", // 118 "Componist", // 119 - "Band", // 120 *** + "Band", // 120 "Opmerking", // 121 - "Genre", // 122 *** + "Genre", // 122 "Nieuws", // 123 "Lokaal nieuws", // 124 "Financiële beurs", // 125 - "Sport", // 126 *** + "Sport", // 126 "Loterij", // 127 "Horoscoop", // 128 "Dagelijkse grap", // 129 "Gezondheid", // 130 "Evenement", // 131 - "Scene", // 132 *** + "Scene", // 132 "Bioscoop", // 133 - "TV", // 134 *** + "TV", // 134 "Datum/tijd", // 135 "Weer", // 136 "Verkeer", // 137 - "Alarm", // 138 *** + "Alarm", // 138 "Advertentie", // 139 - "Website", // 140 *** + "Website", // 140 "Overige", // 141 "Korte naam", // 142 "Lange naam", // 143 "Nu", // 144 "Straks", // 145 "Deel", // 146 - "Host", // 147 *** + "Host", // 147 "Regiseur", // 148 "Frequentie", // 149 - "Homepage", // 150 *** + "Homepage", // 150 "Subkanaal", // 151 - "Hotline", // 152 *** + "Hotline", // 152 "Studio telefoon", // 153 "Telefoon", // 154 - "SMS studio", // 155 *** - "SMS", // 156 *** - "Email hotline", // 157 *** - "Email studio", // 158 *** - "Email", // 159 *** - "Chat", // 160 *** + "SMS studio", // 155 + "SMS", // 156 + "Email hotline", // 157 + "Email studio", // 158 + "Email", // 159 + "Chat", // 160 "Chat centrum", // 161 "Stemming", // 162 "Stemcentrum", // 163 @@ -438,9 +440,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Aankoop", // 167 "Verkrijg data", // 168 "MW standaard\nstapgrootte", // 169 - "Tangerine", // 170 *** - "Ocean", // 171 *** - "Indigo", // 172 *** + "Tangerine", // 170 + "Ocean", // 171 + "Indigo", // 172 "Batterij weergave", // 173 "Toon spanning", // 174 "Toon percentage", // 175 @@ -460,14 +462,14 @@ static const char* const myLanguage[18][260] PROGMEM = { "ALGEMEEN", // 189 "GELUID", // 190 "SCHERM", // 191 - "RDS", // 192 *** - "FM", // 193 *** - "AM", // 194 *** + "RDS", // 192 + "FM", // 193 + "AM", // 194 "VERBIND", // 195 "DX MODE", // 196 - "Stationlist ID", // 197 *** - "AM AGC", // 198 *** - "FM deemphasis", // 199 *** + "Stationlist ID", // 197 + "AM AGC", // 198 + "FM deemphasis", // 199 "Laag", // 200 "Middel", // 201 "Alles", // 202 @@ -527,7 +529,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Volksmuziek", // 256 "Documentaires", // 257 "Alarmtest", // 258 - "Alarm ! ALARM !" // 259 + "Alarm ! ALARM !", // 259 + "Initieel", // 260 + "Altijd" //261 }, { "Polski", // Polish @@ -576,9 +580,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Łagodne wyciszanie FM", // 43 "Łagodne wyciszanie AM", // 44 "Beep na krawędzi pasma", // 45 - "Region", // 46 *** + "Region", // 46 "Europa", // 47 - "Ameryka", // 48 *** + "Ameryka", // 48 "Pokaż podkreślenia w RDS", // 49 "Tryb USB", // 50 "Wi-Fi aktywne", // 51 @@ -608,9 +612,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Wyłącznik czasowy", // 75 "Wyłącz ekran", // 76 "Motyw", // 77 - "Skórka", // 78 *** + "Skórka", // 78 "Wykryte aplikacje", // 79 - "Min.", // 80 *** + "Min.", // 80 "Dynam. prędkość SPI", // 81 "Czułość skanowania", // 82 "BRAK", // 83 @@ -632,18 +636,18 @@ static const char* const myLanguage[18][260] PROGMEM = { "Używaj AF", // 99 "Wybierz pasmo FM", // 100 "Wybierz pasmo AM", // 101 - "LW", // 102 *** - "MW", // 103 *** - "SW", // 104 *** - "FM", // 105 *** - "OIRT", // 106 *** + "LW", // 102 + "MW", // 103 + "SW", // 104 + "FM", // 105 + "OIRT", // 106 "Stroj. gdy ekran wył.", // 107 "Wybór modelu", // 108 "Podstawowy (ILI9341)", // 109 "Przenośny (ILI9341)", // 110 "Przenoś. dotyk. (ILI9341)", // 111 "Tytuł", // 112 - "Album", // 113 *** + "Album", // 113 "Ścieżka", // 114 "Wykonawca", // 115 "Kompozycja", // 116 @@ -656,7 +660,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "Wiadomości", // 123 "Lokalne wiadomości", // 124 "Giełda", // 125 - "Sport", // 126 *** + "Sport", // 126 "Loteria", // 127 "Horoskop", // 128 "Codzienny zwrot", // 129 @@ -664,11 +668,11 @@ static const char* const myLanguage[18][260] PROGMEM = { "Wydarzenie", // 131 "Scena", // 132 "Kino", // 133 - "TV", // 134 *** + "TV", // 134 "Data/Godzina", // 135 "Pogoda", // 136 "Wiadom. drogowe", // 137 - "Alarm", // 138 *** + "Alarm", // 138 "Reklama", // 139 "Strona internet.", // 140 "Inne", // 141 @@ -686,10 +690,10 @@ static const char* const myLanguage[18][260] PROGMEM = { "Telefon do studia", // 153 "Telefon", // 154 "SMS do studia", // 155 - "SMS", // 156 *** + "SMS", // 156 "Email do gorącej linii", // 157 "Email do studia", // 158 - "Email", // 159 *** + "Email", // 159 "Czat", // 160 "Centrum czatu", // 161 "Pytanie do głosowania", // 162 @@ -700,9 +704,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Zakup", // 167 "Pobierz dane", // 168 "Domyślny krok MW", // 169 - "Mandarynka", // 170 *** - "Ocean", // 171 *** - "Indygo", // 172 *** + "Mandarynka", // 170 + "Ocean", // 171 + "Indygo", // 172 "Opcje baterii", // 173 "Pokaż napięcie", // 174 "Pokaż procent naład.", // 175 @@ -722,9 +726,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "GŁÓWNE", // 189 "DŹWIĘK", // 190 "WYŚWIETLACZ", // 191 - "RDS", // 192 *** - "FM", // 193 *** - "AM", // 194 *** + "RDS", // 192 + "FM", // 193 + "AM", // 194 "ŁĄCZNOŚĆ", // 195 "TRYB DX", // 196 "Identyfikator Stationlist", // 197 @@ -789,7 +793,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Muzyka Ludowa", // 256 "Dokument", // 257 "Alarm Test", // 258 - "Alarm" // 259 + "Alarm", // 259 + "Initial", // 260 + "Always" //261 }, { "Hrvatski", // Croatian @@ -835,8 +841,8 @@ static const char* const myLanguage[18][260] PROGMEM = { "Odaberite jezik", // 40 "PRITISNITE MODE ZA IZLAZ I SPREMANJE", // 41 "Uključeno", // 42 - "Softmute FM", // 43 *** - "Softmute AM", // 44 *** + "Softmute FM", // 43 + "Softmute AM", // 44 "Beep na rubu frekv. opsega", // 45 "Regija", // 46 "Europa", // 47 @@ -870,7 +876,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "Time-out timer", // 75 "Zaslon isključiti", // 76 "Tema dizajna", // 77 - "Skin", // 78 *** + "Skin", // 78 "Otkrivene aplikacije", // 79 "Min.", // 80 "Dinamička SPI brzina", // 81 @@ -1051,7 +1057,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Narodna glazba", // 256 "Dokumentarno", // 257 "Testni alarm!", // 258 - "Alarm !" // 259 + "Alarm !", // 259 + "Initial", // 260 + "Always" //261 }, { "Ελληνικά", // Greek @@ -1132,7 +1140,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "Χρονοδιακόπτης λήξης", // 75 "Οθόνη σβηστή", // 76 "Θέμα", // 77 - "Γραφικό\nπεριβάλλον", // 78 *** + "Γραφικό\nπεριβάλλον", // 78 "Εντοπισμένες εφαρμογές", // 79 "Λεπτά", // 80 "Δυναμική ταχύτητα\nSPI", // 81 @@ -1188,7 +1196,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "Γεγονός", // 131 "Σκηνή", // 132 "Σινεμά", // 133 - "TV", // 134 *** + "TV", // 134 "Ημερομηνία/ώρα", // 135 "Καιρός", // 136 "Κυκλοφορία", // 137 @@ -1210,7 +1218,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "Τηλ. στούντιο", // 153 "Τηλέφωνο", // 154 "SMS στούντιο", // 155 - "SMS", // 156 *** + "SMS", // 156 "Email επικοινωνίας", // 157 "Email στούντιο", // 158 "Email", // 159 @@ -1313,7 +1321,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Παραδοσιακή μουσική", // 256 "Ντοκιμαντέρ", // 257 "Δοκιμή συναγερμού", // 258 - "Συναγερμός!!!" // 259 + "Συναγερμός!!!", // 259 + "Initial", // 260 + "Always" //261 }, { "Română", // Romanian @@ -1575,7 +1585,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Muzică Populară", // 256 "Documentar", // 257 "Test Alarmă", // 258 - "Alarmă !" // 259 + "Alarmă !", // 259 + "Initial", // 260 + "Always" //261 }, { "Deutsch", // German @@ -1656,7 +1668,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "Abschaltzeit", // 75 "Bildschirm aus", // 76 "Thema", // 77 - "Oberfläche", // 78 *** + "Oberfläche", // 78 "Erkannte Applikationen", // 79 "Min.", // 80 "Dynamische \nSPI-Geschw.", // 81 @@ -1837,7 +1849,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Folklore", // 256 "Feature", // 257 "Alarm Test", // 258 - "Alarm! Alarm!" // 259 + "Alarm! Alarm!", // 259 + "Initial", // 260 + "Always" //261 }, { "Český", // Czech @@ -1883,15 +1897,15 @@ static const char* const myLanguage[18][260] PROGMEM = { "Vyberte jazyk", // 40 "ZMÁČKNĚTE MODE PRO ULOŽENÍ", // 41 "Zap.", // 42 - "Softmute FM", // 43 *** - "Softmute AM", // 44 *** + "Softmute FM", // 43 + "Softmute AM", // 44 "Pípnout na konci pásma", // 45 - "Region", // 46 *** + "Region", // 46 "Evropa", // 47 - "Amerika", // 48 *** + "Amerika", // 48 "Zobrazit podtržítka v RDS", // 49 "Režim USB", // 50 - "Wi-Fi aktivní", // 51 *** + "Wi-Fi aktivní", // 51 "Konfigurace Wi-Fi", // 52 "Připojte se k: ", // 53 "pro nakonfigurování Wi-Fi", // 54 @@ -1900,7 +1914,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "PŘIPOJENO!", // 57 "Stationlist IP", // 58 "Zobrazit SW pásmo", // 59 - "RDS filtr", // 60 *** + "RDS filtr", // 60 "Zobrazit chyby v PI", // 61 "Použít squelch", // 62 "Graf modulace", // 63 @@ -1918,14 +1932,14 @@ static const char* const myLanguage[18][260] PROGMEM = { "Časovač vypnutí", // 75 "Vypnutí obrazovky", // 76 "Vzhled", // 77 - "Skin", // 78 *** + "Skin", // 78 "Detekované aplikace", // 79 - "minut", // 80 *** - "Dynamická rychlost SPI", // 81 *** + "minut", // 80 + "Dynamická rychlost SPI", // 81 "Citlivost skenu", // 82 "žádné", // 83 "Verze softwaru", // 84 - "Font frekvence", // 85 *** + "Font frekvence", // 85 "Auto", // 86 "AF nedostupné", // 87 "EON nedostupný", // 88 @@ -1942,77 +1956,77 @@ static const char* const myLanguage[18][260] PROGMEM = { "AF:", // 99 "Zvolte FM pásmo:", // 100 "Zvolte AM pásmo:", // 101 - "LW", // 102 *** - "MW", // 103 *** - "SW", // 104 *** - "FM", // 105 *** - "OIRT", // 106 *** - "Ladění při vypnuté obrazovce", // 107 *** + "LW", // 102 + "MW", // 103 + "SW", // 104 + "FM", // 105 + "OIRT", // 106 + "Ladění při vypnuté obrazovce", // 107 "Volba modelu", // 108 "Základní (ILI9341)", // 109 "Přenosný (ILI9341)", // 110 "Přenosný-dotykový(ILI9341)", // 111 - "Title", // 112 *** - "Album", // 113 *** - "Track", // 114 *** - "Artist", // 115 *** - "Composition", // 116 *** - "Movement", // 117 *** - "Conductor", // 118 *** - "Composer", // 119 *** - "Band", // 120 *** - "Comment", // 121 *** - "Genre", // 122 *** - "News", // 123 *** - "Local news", // 124 *** - "Stockmarket", // 125 *** - "Sport", // 126 *** - "Lottery", // 127 *** - "Horoscope", // 128 *** - "Daily Diversion", // 129 *** - "Health", // 130 *** - "Event", // 131 *** - "Scene", // 132 *** - "Cinema", // 133 *** - "TV", // 134 *** - "Date/time", // 135 *** - "Weather", // 136 *** - "Traffic", // 137 *** - "Alarm", // 138 *** - "Advertisement", // 139 *** - "Website", // 140 *** - "Other", // 141 *** - "Short PS", // 142 *** - "Long PS", // 143 *** - "Now", // 144 *** - "Next", // 145 *** - "Part", // 146 *** - "Host", // 147 *** - "Editor", // 148 *** - "Frequency", // 149 *** - "Homepage", // 150 *** - "Subchannel", // 151 *** - "Hotline", // 152 *** - "Studio phone", // 153 *** - "Phone", // 154 *** - "SMS studio", // 155 *** - "SMS", // 156 *** - "Email hotline", // 157 *** - "Email studio", // 158 *** - "Email", // 159 *** - "Chat", // 160 *** - "Chat centre", // 161 *** - "Vote question", // 162 *** - "Vote centre", // 163 *** - "Place", // 164 *** - "Appointment", // 165 *** - "Identifier", // 166 *** - "Purchase", // 167 *** - "Get data", // 168 *** - "Výchozí krok ladění MW", // 169 *** - "Tangerine", // 170 *** - "Ocean", // 171 *** - "Indigo", // 172 *** + "Title", // 112 + "Album", // 113 + "Track", // 114 + "Artist", // 115 + "Composition", // 116 + "Movement", // 117 + "Conductor", // 118 + "Composer", // 119 + "Band", // 120 + "Comment", // 121 + "Genre", // 122 + "News", // 123 + "Local news", // 124 + "Stockmarket", // 125 + "Sport", // 126 + "Lottery", // 127 + "Horoscope", // 128 + "Daily Diversion", // 129 + "Health", // 130 + "Event", // 131 + "Scene", // 132 + "Cinema", // 133 + "TV", // 134 + "Date/time", // 135 + "Weather", // 136 + "Traffic", // 137 + "Alarm", // 138 + "Advertisement", // 139 + "Website", // 140 + "Other", // 141 + "Short PS", // 142 + "Long PS", // 143 + "Now", // 144 + "Next", // 145 + "Part", // 146 + "Host", // 147 + "Editor", // 148 + "Frequency", // 149 + "Homepage", // 150 + "Subchannel", // 151 + "Hotline", // 152 + "Studio phone", // 153 + "Phone", // 154 + "SMS studio", // 155 + "SMS", // 156 + "Email hotline", // 157 + "Email studio", // 158 + "Email", // 159 + "Chat", // 160 + "Chat centre", // 161 + "Vote question", // 162 + "Vote centre", // 163 + "Place", // 164 + "Appointment", // 165 + "Identifier", // 166 + "Purchase", // 167 + "Get data", // 168 + "Výchozí krok ladění MW", // 169 + "Tangerine", // 170 + "Ocean", // 171 + "Indigo", // 172 "Úroveň nabití baterie", // 173 "Zobrazit napětí", // 174 "Zobrazit procenta", // 175 @@ -2032,14 +2046,14 @@ static const char* const myLanguage[18][260] PROGMEM = { "HLAVNÍ", // 189 "ZVUK", // 190 "DISPLEJ", // 191 - "RDS", // 192 *** - "FM", // 193 *** - "AM", // 194 *** + "RDS", // 192 + "FM", // 193 + "AM", // 194 "KONEKTIVITA", // 195 "DX REŽIM", // 196 - "Stationlist ID", // 197 *** + "Stationlist ID", // 197 "AM ATT", // 198 - "FM deemfáze", // 199 *** + "FM deemfáze", // 199 "Malé", // 200 "Velké", // 201 "Vše", // 202 @@ -2099,7 +2113,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Folkova Hudba", // 256 "Dokumenty", // 257 "Alarm Test", // 258 - "Alarm – Alarm !" // 259 + "Alarm – Alarm !", // 259 + "Initial", // 260 + "Always" //261 }, { "SloveSlovenský", // Slovak @@ -2145,15 +2161,15 @@ static const char* const myLanguage[18][260] PROGMEM = { "Vyberte jazyk", // 40 "STLAČTE MODE PRE ULOŽENIE", // 41 "Zap", // 42 - "Softmute FM", // 43 *** - "Softmute AM", // 44 *** + "Softmute FM", // 43 + "Softmute AM", // 44 "Pípnutie na konci pásma", // 45 - "Region", // 46 *** + "Region", // 46 "Európa", // 47 - "America", // 48 *** + "America", // 48 "Zobraziť podtržítka v RDS", // 49 "USB mód", // 50 - "Wi-Fi active", // 51 *** + "Wi-Fi active", // 51 "Konfigurace Wi-Fi", // 52 "Pripojte sa k: ", // 53 "pre nakonfigurovánie Wi-Fi", // 54 @@ -2162,7 +2178,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "PRIPOJENÉ!", // 57 "Stationlist IP", // 58 "Zobraziť SW pásmo", // 59 - "RDS filter", // 60 *** + "RDS filter", // 60 "Zobraziť chyby v PI", // 61 "Zobraziť squelch", // 62 NEEDS NEW TRANSLATION, ENGLISH: USE SQUELCH "Graf modulácie", // 63 @@ -2180,14 +2196,14 @@ static const char* const myLanguage[18][260] PROGMEM = { "Čas", // 75 "Vypnutie obrazovky", // 76 "Vzhľad", // 77 - "Skin", // 78 *** + "Skin", // 78 "Detegované aplikácie", // 79 - "Min.", // 80 *** - "Dynamic SPI speed", // 81 *** + "Min.", // 80 + "Dynamic SPI speed", // 81 "Scan sensitivity", // 82 "NONE", // 83 represents: AM/FM bands unavailable(please delete this note after translation) "Software version", // 84 - "Frequency font", // 85 *** + "Frequency font", // 85 "Auto", // 86 "AF nedostupné", // 87 "EON nedostupný", // 88 @@ -2204,77 +2220,77 @@ static const char* const myLanguage[18][260] PROGMEM = { "AF:", // 99 "Zvoľte FM pásmo:", // 100 "Zvoľte AM pásmo:", // 101 - "LW", // 102 *** - "MW", // 103 *** - "SW", // 104 *** - "FM", // 105 *** - "OIRT", // 106 *** - "Rotating while screen off", // 107 *** + "LW", // 102 + "MW", // 103 + "SW", // 104 + "FM", // 105 + "OIRT", // 106 + "Rotating while screen off", // 107 "Voľba modelu", // 108 "Základ (ILI9341)", // 109 "Prenosný (ILI9341)", // 110 "Prenosný - dotykový (ILI9341)", // 111 - "Title", // 112 *** - "Album", // 113 *** - "Track", // 114 *** - "Artist", // 115 *** - "Composition", // 116 *** - "Movement", // 117 *** - "Conductor", // 118 *** - "Composer", // 119 *** - "Band", // 120 *** - "Comment", // 121 *** - "Genre", // 122 *** - "News", // 123 *** - "Local news", // 124 *** - "Stockmarket", // 125 *** - "Sport", // 126 *** - "Lottery", // 127 *** - "Horoscope", // 128 *** - "Daily Diversion", // 129 *** - "Health", // 130 *** - "Event", // 131 *** - "Scene", // 132 *** - "Cinema", // 133 *** - "TV", // 134 *** - "Date/time", // 135 *** - "Weather", // 136 *** - "Traffic", // 137 *** - "Alarm", // 138 *** - "Advertisement", // 139 *** - "Website", // 140 *** - "Other", // 141 *** - "Short PS", // 142 *** - "Long PS", // 143 *** - "Now", // 144 *** - "Next", // 145 *** - "Part", // 146 *** - "Host", // 147 *** - "Editor", // 148 *** - "Frequency", // 149 *** - "Homepage", // 150 *** - "Subchannel", // 151 *** - "Hotline", // 152 *** - "Studio phone", // 153 *** - "Phone", // 154 *** - "SMS studio", // 155 *** - "SMS", // 156 *** - "Email hotline", // 157 *** - "Email studio", // 158 *** - "Email", // 159 *** - "Chat", // 160 *** - "Chat centre", // 161 *** - "Vote question", // 162 *** - "Vote centre", // 163 *** - "Place", // 164 *** - "Appointment", // 165 *** - "Identifier", // 166 *** - "Purchase", // 167 *** - "Get data", // 168 *** - "MW default stepsize", // 169 *** - "Tangerine", // 170 *** - "Ocean", // 171 *** - "Indigo", // 172 *** + "Title", // 112 + "Album", // 113 + "Track", // 114 + "Artist", // 115 + "Composition", // 116 + "Movement", // 117 + "Conductor", // 118 + "Composer", // 119 + "Band", // 120 + "Comment", // 121 + "Genre", // 122 + "News", // 123 + "Local news", // 124 + "Stockmarket", // 125 + "Sport", // 126 + "Lottery", // 127 + "Horoscope", // 128 + "Daily Diversion", // 129 + "Health", // 130 + "Event", // 131 + "Scene", // 132 + "Cinema", // 133 + "TV", // 134 + "Date/time", // 135 + "Weather", // 136 + "Traffic", // 137 + "Alarm", // 138 + "Advertisement", // 139 + "Website", // 140 + "Other", // 141 + "Short PS", // 142 + "Long PS", // 143 + "Now", // 144 + "Next", // 145 + "Part", // 146 + "Host", // 147 + "Editor", // 148 + "Frequency", // 149 + "Homepage", // 150 + "Subchannel", // 151 + "Hotline", // 152 + "Studio phone", // 153 + "Phone", // 154 + "SMS studio", // 155 + "SMS", // 156 + "Email hotline", // 157 + "Email studio", // 158 + "Email", // 159 + "Chat", // 160 + "Chat centre", // 161 + "Vote question", // 162 + "Vote centre", // 163 + "Place", // 164 + "Appointment", // 165 + "Identifier", // 166 + "Purchase", // 167 + "Get data", // 168 + "MW default stepsize", // 169 + "Tangerine", // 170 + "Ocean", // 171 + "Indigo", // 172 "Nastavenia batérie", // 173 "Zobraziť napätie", // 174 "Zobraziť percentá", // 175 @@ -2294,14 +2310,14 @@ static const char* const myLanguage[18][260] PROGMEM = { "HLAVNÝ", // 189 "ZVUK", // 190 "DISPLEJ", // 191 - "RDS", // 192 *** - "FM", // 193 *** - "AM", // 194 *** + "RDS", // 192 + "FM", // 193 + "AM", // 194 "PRIPOJENIE", // 195 "DX MODE", // 196 - "Stationlist ID", // 197 *** + "Stationlist ID", // 197 "AM ATT", // 198 - "FM deemphasis", // 199 *** + "FM deemphasis", // 199 "Malý", // 200 "Veľký", // 201 "Všetko", // 202 @@ -2361,7 +2377,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Folk Music", // 256 "Documentary", // 257 "Alarm Test", // 258 - "Alarm!!!" // 259 + "Alarm!!!", // 259 + "Initial", // 260 + "Always" //261 }, { "Français", // French @@ -2442,21 +2460,21 @@ static const char* const myLanguage[18][260] PROGMEM = { "Temporisateur", // 75 "Affichage éteint", // 76 "Thème", // 77 - "Skin", // 78 *** + "Skin", // 78 "Applications détectées", // 79 "Min.", // 80 "Vitesse SPI\ndynamique", // 81 "Sensibilité\ndu scanne ", // 82 "AUCUNE", // 83 "Version de logiciel", // 84 - "Police de fréquence", // 85 *** + "Police de fréquence", // 85 "Auto", // 86 "AF indisponible", // 87 "EON indisponible", // 88 "RT+ indisponible", // 89 "Valeur du pas FM\npar défaut", // 90 "Économiseur d'affichage", // 91 - "Sec", // 92 *** + "Sec", // 92 "APPUYEZ SUR BAND POUR FERMER", // 93 "1% luminosité", // 94 "25% luminosité", // 95 @@ -2623,7 +2641,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Folklore", // 256 "Documentaire", // 257 "TEST", // 258 - "Alerte !" // 259 + "Alerte !", // 259 + "Initial", // 260 + "Always" //261 }, { "Български", // Bulgarian @@ -2700,18 +2720,18 @@ static const char* const myLanguage[18][260] PROGMEM = { "Главен код:", // 71 "Сътрудници:", // 72 "Неизвестно", // 73 - "Screensaver options", // 74 *** - "Time-out timer", // 75 *** + "Screensaver options", // 74 + "Time-out timer", // 75 "Изключен дисплей", // 76 "Тема", // 77 - "Skin", // 78 *** - "Detected applications", // 79 *** + "Skin", // 78 + "Detected applications", // 79 "Мин.", // 80 - "Dynamic SPI speed", // 81 *** + "Dynamic SPI speed", // 81 "Scan sensitivity", // 82 "NONE", // 83 represents: AM/FM bands unavailable(please delete this note after translation) "Software version", // 84 - "Frequency font", // 85 *** + "Frequency font", // 85 "Auto", // 86 "AF опцията не е налична", // 87 "EON опцията не е налична", // 88 @@ -2742,7 +2762,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "Албум", // 113 "Песен", // 114 "Изпълнител", // 115 - "Composition", // 116 *** + "Composition", // 116 "Действие", // 117 "Диригент", // 118 "Композитор", // 119 @@ -2755,9 +2775,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Спорт", // 126 "Лото", // 127 "Хороскоп", // 128 - "Daily Diversion", // 129 *** + "Daily Diversion", // 129 "Здраве", // 130 - "Event", // 131 *** + "Event", // 131 "Сцена", // 132 "Кино", // 133 "ТВ", // 134 @@ -2767,50 +2787,50 @@ static const char* const myLanguage[18][260] PROGMEM = { "Аларма", // 138 "Реклама", // 139 "Уеб страница", // 140 - "Other", // 141 *** - "Short PS", // 142 *** - "Long PS", // 143 *** - "Now", // 144 *** + "Other", // 141 + "Short PS", // 142 + "Long PS", // 143 + "Now", // 144 "Следващ", // 145 - "Part", // 146 *** - "Host", // 147 *** - "Editor", // 148 *** + "Part", // 146 + "Host", // 147 + "Editor", // 148 "Честота", // 149 "Начална страница", // 150 - "Subchannel", // 151 *** + "Subchannel", // 151 "Гореща линия", // 152 "Студиен тел", // 153 "Телефон", // 154 - "SMS studio", // 155 *** - "SMS", // 156 *** + "SMS studio", // 155 + "SMS", // 156 "Имейл гореща линия", // 157 "Имейл до студиото", // 158 "Имейл", // 159 "Чат", // 160 "Чат център", // 161 - "Vote question", // 162 *** - "Vote centre", // 163 *** - "Place", // 164 *** - "Appointment", // 165 *** + "Vote question", // 162 + "Vote centre", // 163 + "Place", // 164 + "Appointment", // 165 "Идентификатор", // 166 "Покупка", // 167 "Получи данни", // 168 - "MW default stepsize", // 169 *** - "Tangerine", // 170 *** + "MW default stepsize", // 169 + "Tangerine", // 170 "Океан", // 171 "Индиго", // 172 - "Battery options", // 173 *** - "Show voltage", // 174 *** - "Show percentage", // 175 *** - "RT buffering", // 176 *** + "Battery options", // 173 + "Show voltage", // 174 + "Show percentage", // 175 + "RT buffering", // 176 "Главни настройки", // 177 "Аудио-настройка", // 178 "Екран-настроийка", // 179 "RDS настройки", // 180 "FM настройки", // 181 "AM/СВ настройки", // 182 - "Connectivity", // 183 *** - "PRESS MODE TO RETURN", // 184 *** + "Connectivity", // 183 + "PRESS MODE TO RETURN", // 184 "AM CoChannelDet", // 185 "CoChannelDet sensitivity", // 186 "AM CoChannelDet threshold", // 187 @@ -2818,18 +2838,18 @@ static const char* const myLanguage[18][260] PROGMEM = { "ГЛАВНО МЕНЮ", // 189 "АУДИО", // 190 "ЕКРАН", // 191 - "RDS", // 192 *** + "RDS", // 192 "УКВ (FM)", // 193 "СВ (AM)", // 194 "ВРЪЗКА", // 195 "DX MODE", // 196 "Stationlist Идентификатор", // 197 "СВ атенюатор", // 198 - "FM deemphasis", // 199 *** - "Small", // 200 *** - "Large", // 201 *** + "FM deemphasis", // 199 + "Small", // 200 + "Large", // 201 "Всички", // 202 - "Fast PS", // 203 *** + "Fast PS", // 203 "Default", // 204 "Mute screen on\nXDRGTK connection", // 205 "FMSI stereo\nimprovement", // 206 @@ -2841,7 +2861,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "Start DX scanning", // 212 "FM DX Options", // 213 "Abort! The start channel\nis marked as skipped.", // 214 - "Auto sort AF", // 215 *** + "Auto sort AF", // 215 "Memory channels", // 216 "Exclude", // 217 "Only", // 218 @@ -2885,7 +2905,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Народна музика", // 256 "Документалистика", // 257 "Внимание тест", // 258 - "Тревога!" // 259 + "Тревога!", // 259 + "Initial", // 260 + "Always" //261 }, { "Русский", // Russian @@ -2966,14 +2988,14 @@ static const char* const myLanguage[18][260] PROGMEM = { "Глубокий сон", // 75 "Погасить экран", // 76 "Тема", // 77 - "Вид (Skin)", // 78 *** + "Вид (Skin)", // 78 "Обнаруженные приложения", // 79 "мин.", // 80 - "Частота шины дисплея SPI", // 81 *** + "Частота шины дисплея SPI", // 81 "Чувствительность сканирования", // 82 "Не использовать AM", // 83 "Версия прошивки -", // 84 - "Шрифт индикации частоты", // 85 *** + "Шрифт индикации частоты", // 85 "Auto", // 86 "Нет данных", // 87 "Нет данных", // 88 @@ -3147,7 +3169,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Народная музыка", // 256 "Документалистика", // 257 "Внимание тест", // 258 - "Тревога!" // 259 + "Тревога!", // 259 + "Initial", // 260 + "Always" //261 }, { "Українська", // Ukranian @@ -3228,14 +3252,14 @@ static const char* const myLanguage[18][260] PROGMEM = { "Таймер тайм-ауту", // 75 "Екран вимкнено", // 76 "Тема", // 77 - "Skin", // 78 *** + "Skin", // 78 "Виявлені програми", // 79 "мін.", // 80 - "Швидкість шини дисплея (SPI)", // 81 *** + "Швидкість шини дисплея (SPI)", // 81 "Чутливість сканування", // 82 "NONE", // 83 represents: AM/FM bands unavailable(please delete this note after translation) "Версія ПО", // 84 - "Frequency font", // 85 *** + "Frequency font", // 85 "Auto", // 86 "Немає доступних AF", // 87 "Немає доступного EON", // 88 @@ -3252,11 +3276,11 @@ static const char* const myLanguage[18][260] PROGMEM = { "Режим AF:", // 99 "Виберіть FM-діапазон:", // 100 "Виберіть AM-діапазон:", // 101 - "LW", // 102 *** - "MW", // 103 *** - "SW", // 104 *** - "FM", // 105 *** - "OIRT", // 106 *** + "LW", // 102 + "MW", // 103 + "SW", // 104 + "FM", // 105 + "OIRT", // 106 "Обертання, коли екран вимкнено", // 107 "Вибір моделі", // 108 "Стандартний (ILI9341)", // 109 @@ -3306,10 +3330,10 @@ static const char* const myLanguage[18][260] PROGMEM = { "Телефон студії", // 153 "Телефон", // 154 "SMS студії", // 155 - "SMS", // 156 *** + "SMS", // 156 "Email гарячої лінії", // 157 "Email студії", // 158 - "Email", // 159 *** + "Email", // 159 "Чат", // 160 "Чат-центр", // 161 "Питання голосування", // 162 @@ -3319,7 +3343,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "Ідентифікатор", // 166 "Покупка", // 167 "Отримати дані", // 168 - "MW default stepsize", // 169 *** + "MW default stepsize", // 169 "Мандариновий", // 170 "Океан", // 171 "Індиго", // 172 @@ -3342,14 +3366,14 @@ static const char* const myLanguage[18][260] PROGMEM = { "ОСНОВНОЇ", // 189 "АУДІО", // 190 "ДИСПЛЕЙ", // 191 - "RDS", // 192 *** - "FM", // 193 *** - "AM", // 194 *** + "RDS", // 192 + "FM", // 193 + "AM", // 194 "ПІДКЛЮЧЕННЯ", // 195 "DX MODE", // 196 - "Stationlist ID", // 197 *** + "Stationlist ID", // 197 "AM ATT", // 198 - "FM deemphasis", // 199 *** + "FM deemphasis", // 199 "Мало", // 200 "Багато", // 201 "Усе", // 202 @@ -3409,7 +3433,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Folk Music", // 256 "Documentary", // 257 "Alarm Test", // 258 - "Alarm!!!" // 259 + "Alarm!!!", // 259 + "Initial", // 260 + "Always" //261 }, { "Italiano", // Italian @@ -3442,10 +3468,10 @@ static const char* const myLanguage[18][260] PROGMEM = { "Soglia taglio alti", // 27 "Soglia di livello basso", // 28 "Luminosità schermo", // 29 - "Off", // 30 *** + "Off", // 30 "Schermo disattivato!", // 31 "Deseleziona box RF+ per audio", // 32 - "OFF", // 33 *** + "OFF", // 33 "SCANSIONE...", // 34 "Ricevitore non rilevato", // 35 "Guadagno antenna AM", // 36 @@ -3454,13 +3480,13 @@ static const char* const myLanguage[18][260] PROGMEM = { "Lingua", // 39 "Scegli la lingua", // 40 "SALVA E ESCI: PREMI MODO", // 41 - "On", // 42 *** - "Softmute FM", // 43 *** - "Softmute AM", // 44 *** + "On", // 42 + "Softmute FM", // 43 + "Softmute AM", // 44 "Beep sul limite di banda", // 45 "Regione", // 46 "Europa", // 47 - "America", // 48 *** + "America", // 48 "Mostra trattino basso in RDS", // 49 "Modalità USB", // 50 "Wi-Fi attivo", // 51 @@ -3486,18 +3512,18 @@ static const char* const myLanguage[18][260] PROGMEM = { "Codice principale:", // 71 "Collaboratori: ", // 72 "Sconosciuto", // 73 - "Opzioni salvaschermo", // 74 *** - "Tempo di time-out", // 75 *** + "Opzioni salvaschermo", // 74 + "Tempo di time-out", // 75 "Schermo spento", // 76 "Motivo schermo", // 77 - "Skin", // 78 *** - "Applicazioni rilevate", // 79 *** - "Min.", // 80 *** - "Velocità SPI Dinamico", // 81 *** + "Skin", // 78 + "Applicazioni rilevate", // 79 + "Min.", // 80 + "Velocità SPI Dinamico", // 81 "Sensibilità scansione", // 82 "ASSENTE", // 83 "Versione Software", // 84 - "Font frequenza", // 85 *** + "Font frequenza", // 85 "Auto", // 86 "AF non disponibile", // 87 "EON non disponibile", // 88 @@ -3514,108 +3540,108 @@ static const char* const myLanguage[18][260] PROGMEM = { "Utilizzare AF:", // 99 "Selezionare banda FM:", // 100 "Selezionare banda AM:", // 101 - "LW", // 102 *** - "MW", // 103 *** - "SW", // 104 *** - "FM", // 105 *** - "OIRT", // 106 *** - "Rotazione con schermo spento", // 107 *** - "Selezionare modello", // 108 *** - "Base (ILI9341)", // 109 *** - "Portatile (ILI9341)", // 110 *** - "Portatile touch (ILI9341)", // 111 *** - "Titolo", // 112 *** - "Album", // 113 *** - "Traccia", // 114 *** - "Artista", // 115 *** - "Composizione", // 116 *** - "Movimento", // 117 *** - "Direttore", // 118 *** - "Compositore", // 119 *** - "Band", // 120 *** - "Commento", // 121 *** - "Genere", // 122 *** - "Notizie", // 123 *** - "Notizie locali", // 124 *** - "Borsa", // 125 *** - "Sport", // 126 *** - "Lotterie", // 127 *** - "Oroscopo", // 128 *** - "Svago quotidiano", // 129 *** - "Salute", // 130 *** - "Eventi", // 131 *** - "Scena", // 132 *** - "Cinema", // 133 *** - "TV", // 134 *** - "Data/ora", // 135 *** - "Meteo", // 136 *** - "Traffico", // 137 *** - "Allerta", // 138 *** - "Pubblicità", // 139 *** - "Sito Web", // 140 *** - "Altro", // 141 *** - "PS breve", // 142 *** - "PS lungo", // 143 *** - "Adesso", // 144 *** - "Prossimo", // 145 *** - "Parte", // 146 *** - "Conduttore", // 147 *** - "Redattore", // 148 *** - "Frequenza", // 149 *** - "Homepage", // 150 *** - "Sottocanale", // 151 *** - "Telefono emergenze", // 152 *** - "Telefono studio", // 153 *** - "Telefono", // 154 *** - "SMS studio", // 155 *** - "SMS", // 156 *** - "Email emergenze", // 157 *** - "Email studio", // 158 *** - "Email", // 159 *** - "Chat", // 160 *** - "Centro Chat", // 161 *** - "Domanda votazione", // 162 *** - "Centro votazione", // 163 *** - "Luogo", // 164 *** - "Appuntamento", // 165 *** - "Identificatore", // 166 *** - "Acquisto", // 167 *** - "Ottieni dati", // 168 *** - "Passo di sintonia\nMW predefinito", // 169 *** - "Tangerine", // 170 *** - "Ocean", // 171 *** - "Indigo", // 172 *** - "Opzioni batteria", // 173 *** - "Mostra tensione", // 174 *** - "Mostra percentuale", // 175 *** - "Buffering RT", // 176 *** - "Impostazioni principali", // 177 *** - "Impostazioni audio", // 178 *** - "Impostazioni schermo", // 179 *** - "Impostazioni RDS", // 180 *** - "Impostazioni FM", // 181 *** - "Impostazioni AM", // 182 *** - "Connettività", // 183 *** - "PREMI MODO PER CHIUDERE", // 184 *** + "LW", // 102 + "MW", // 103 + "SW", // 104 + "FM", // 105 + "OIRT", // 106 + "Rotazione con schermo spento", // 107 + "Selezionare modello", // 108 + "Base (ILI9341)", // 109 + "Portatile (ILI9341)", // 110 + "Portatile touch (ILI9341)", // 111 + "Titolo", // 112 + "Album", // 113 + "Traccia", // 114 + "Artista", // 115 + "Composizione", // 116 + "Movimento", // 117 + "Direttore", // 118 + "Compositore", // 119 + "Band", // 120 + "Commento", // 121 + "Genere", // 122 + "Notizie", // 123 + "Notizie locali", // 124 + "Borsa", // 125 + "Sport", // 126 + "Lotterie", // 127 + "Oroscopo", // 128 + "Svago quotidiano", // 129 + "Salute", // 130 + "Eventi", // 131 + "Scena", // 132 + "Cinema", // 133 + "TV", // 134 + "Data/ora", // 135 + "Meteo", // 136 + "Traffico", // 137 + "Allerta", // 138 + "Pubblicità", // 139 + "Sito Web", // 140 + "Altro", // 141 + "PS breve", // 142 + "PS lungo", // 143 + "Adesso", // 144 + "Prossimo", // 145 + "Parte", // 146 + "Conduttore", // 147 + "Redattore", // 148 + "Frequenza", // 149 + "Homepage", // 150 + "Sottocanale", // 151 + "Telefono emergenze", // 152 + "Telefono studio", // 153 + "Telefono", // 154 + "SMS studio", // 155 + "SMS", // 156 + "Email emergenze", // 157 + "Email studio", // 158 + "Email", // 159 + "Chat", // 160 + "Centro Chat", // 161 + "Domanda votazione", // 162 + "Centro votazione", // 163 + "Luogo", // 164 + "Appuntamento", // 165 + "Identificatore", // 166 + "Acquisto", // 167 + "Ottieni dati", // 168 + "Passo di sintonia\nMW predefinito", // 169 + "Tangerine", // 170 + "Ocean", // 171 + "Indigo", // 172 + "Opzioni batteria", // 173 + "Mostra tensione", // 174 + "Mostra percentuale", // 175 + "Buffering RT", // 176 + "Impostazioni principali", // 177 + "Impostazioni audio", // 178 + "Impostazioni schermo", // 179 + "Impostazioni RDS", // 180 + "Impostazioni FM", // 181 + "Impostazioni AM", // 182 + "Connettività", // 183 + "PREMI MODO PER CHIUDERE", // 184 "CoChannelDet AM", // 185 "Sensibilità CoChannelDet", // 186 "Soglia CoChannelDet AM", // 187 "Contatore CoChannelDet", // 188 - "PRINCIPALE", // 189 *** - "AUDIO", // 190 *** - "SCHERMO", // 191 *** - "RDS", // 192 *** - "FM", // 193 *** - "AM", // 194 *** - "CONNETTI", // 195 *** + "PRINCIPALE", // 189 + "AUDIO", // 190 + "SCHERMO", // 191 + "RDS", // 192 + "FM", // 193 + "AM", // 194 + "CONNETTI", // 195 "MODO DX", // 196 - "ID Stationlist", // 197 *** + "ID Stationlist", // 197 "ATT AM", // 198 - "Deenfasi FM", // 199 *** - "Piccolo", // 200 *** - "Grande", // 201 *** - "Tutto", // 202 *** - "PS rapido", // 203 *** + "Deenfasi FM", // 199 + "Piccolo", // 200 + "Grande", // 201 + "Tutto", // 202 + "PS rapido", // 203 "Default", // 204 "Spegni schermo se\nconnesso a XDRGTK", // 205 "FMSI audio stereofonico ottimizzato", // 206 @@ -3671,7 +3697,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Musica Folk", // 256 "Documentari", // 257 "Test Allarme", // 258 - "!!Allarme!!" // 259 + "!!Allarme!!", // 259 + "Initial", // 260 + "Always" //261 }, { "Simplified Chinese", // Simplified Chinese @@ -3707,7 +3735,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "关闭", // 30 "屏幕静音!", // 31 "点击RF+按钮解除静音", // 32 - "OFF", // 33 *** + "OFF", // 33 "搜索中...", // 34 "未检测到收音芯片", // 35 "AM 天线增益", // 36 @@ -3752,14 +3780,14 @@ static const char* const myLanguage[18][260] PROGMEM = { "倒计时关机", // 75 "关闭屏幕", // 76 "主题&颜色", // 77 - "布局", // 78 *** - "Detected applications", // 79 *** + "布局", // 78 + "Detected applications", // 79 "分钟", // 80 "调整SPI通讯速率", // 81 "搜索灵敏度", // 82 "不可用", // 83 "固件版本", // 84 - "频率字体", // 85 *** + "频率字体", // 85 "自动", // 86 "AF 不可用", // 87 "EON 不可用", // 88 @@ -3776,74 +3804,74 @@ static const char* const myLanguage[18][260] PROGMEM = { "使用 AF", // 99 "选择FM波段", // 100 "选择AM波段", // 101 - "LW", // 102 *** - "MW", // 103 *** - "SW", // 104 *** - "FM", // 105 *** - "OIRT", // 106 *** + "LW", // 102 + "MW", // 103 + "SW", // 104 + "FM", // 105 + "OIRT", // 106 "熄屏操作飞梭", // 107 "硬件型号选择", // 108 "原版(ILI9341)", // 109 "便携版(ILI9341)", // 110 "便携电阻触摸版(ILI9341)", // 111 - "Title", // 112 *** - "Album", // 113 *** - "Track", // 114 *** - "Artist", // 115 *** - "Composition", // 116 *** - "Movement", // 117 *** - "Conductor", // 118 *** - "Composer", // 119 *** - "Band", // 120 *** - "Comment", // 121 *** - "Genre", // 122 *** - "News", // 123 *** - "Local news", // 124 *** - "Stockmarket", // 125 *** - "Sport", // 126 *** - "Lottery", // 127 *** - "Horoscope", // 128 *** - "Daily Diversion", // 129 *** - "Health", // 130 *** - "Event", // 131 *** - "Scene", // 132 *** - "Cinema", // 133 *** - "TV", // 134 *** - "Date/time", // 135 *** - "Weather", // 136 *** - "Traffic", // 137 *** - "Alarm", // 138 *** - "Advertisement", // 139 *** - "Website", // 140 *** - "Other", // 141 *** - "Short PS", // 142 *** - "Long PS", // 143 *** - "Now", // 144 *** - "Next", // 145 *** - "Part", // 146 *** - "Host", // 147 *** - "Editor", // 148 *** - "Frequency", // 149 *** - "Homepage", // 150 *** - "Subchannel", // 151 *** - "Hotline", // 152 *** - "Studio phone", // 153 *** - "Phone", // 154 *** - "SMS studio", // 155 *** - "SMS", // 156 *** - "Email hotline", // 157 *** - "Email studio", // 158 *** - "Email", // 159 *** - "Chat", // 160 *** - "Chat centre", // 161 *** - "Vote question", // 162 *** - "Vote centre", // 163 *** - "Place", // 164 *** - "Appointment", // 165 *** - "Identifier", // 166 *** - "Purchase", // 167 *** - "Get data", // 168 *** - "中波默认步进", // 169 *** + "Title", // 112 + "Album", // 113 + "Track", // 114 + "Artist", // 115 + "Composition", // 116 + "Movement", // 117 + "Conductor", // 118 + "Composer", // 119 + "Band", // 120 + "Comment", // 121 + "Genre", // 122 + "News", // 123 + "Local news", // 124 + "Stockmarket", // 125 + "Sport", // 126 + "Lottery", // 127 + "Horoscope", // 128 + "Daily Diversion", // 129 + "Health", // 130 + "Event", // 131 + "Scene", // 132 + "Cinema", // 133 + "TV", // 134 + "Date/time", // 135 + "Weather", // 136 + "Traffic", // 137 + "Alarm", // 138 + "Advertisement", // 139 + "Website", // 140 + "Other", // 141 + "Short PS", // 142 + "Long PS", // 143 + "Now", // 144 + "Next", // 145 + "Part", // 146 + "Host", // 147 + "Editor", // 148 + "Frequency", // 149 + "Homepage", // 150 + "Subchannel", // 151 + "Hotline", // 152 + "Studio phone", // 153 + "Phone", // 154 + "SMS studio", // 155 + "SMS", // 156 + "Email hotline", // 157 + "Email studio", // 158 + "Email", // 159 + "Chat", // 160 + "Chat centre", // 161 + "Vote question", // 162 + "Vote centre", // 163 + "Place", // 164 + "Appointment", // 165 + "Identifier", // 166 + "Purchase", // 167 + "Get data", // 168 + "中波默认步进", // 169 "桔红", // 170 "海之蓝", // 171 "靛青", // 172 @@ -3871,7 +3899,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "AM 设置", // 194 "连机模式", // 195 "FMDX 选项", // 196 - "Stationlist ID", // 197 *** + "Stationlist ID", // 197 "AM天线衰减", // 198 "FM去加重", // 199 "少量", // 200 @@ -3933,7 +3961,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Folk Music", // 256 "Documentary", // 257 "Alarm Test", // 258 - "Alarm!!!" // 259 + "Alarm!!!", // 259 + "Initial", // 260 + "Always" //261 }, { "Norsk", // Norwegian @@ -3979,10 +4009,10 @@ static const char* const myLanguage[18][260] PROGMEM = { "Velg språk", // 40 "TRYKK MODE FOR Å LAGRE & AVSLUTTE", // 41 "På", // 42 - "Softmute FM", // 43 *** - "Softmute AM", // 44 *** + "Softmute FM", // 43 + "Softmute AM", // 44 "Pip ved båndkant", // 45 - "Region", // 46 *** + "Region", // 46 "Europa", // 47 "Amerika", // 48 "Vis understrek i RDS", // 49 @@ -3996,7 +4026,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "TILKOBLET!", // 57 "Stationlist klient IP", // 58 "Vis SW-bånd", // 59 - "RDS filter", // 60 *** + "RDS filter", // 60 "Vis PI feil", // 61 "Bruk squelch", // 62 "Modulasjonsmåler", // 63 @@ -4014,9 +4044,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Tidsavbruddstimer", // 75 "Skjerm av", // 76 "Tema", // 77 - "Skin", // 78 *** + "Skin", // 78 "Oppdaget applikasjoner", // 79 - "Min.", // 80 *** + "Min.", // 80 "Dynamisk SPI hastighet", // 81 "Skannefølsomhet", // 82 "INGEN", // 83 @@ -4041,40 +4071,40 @@ static const char* const myLanguage[18][260] PROGMEM = { "LB", // 102 "MB", // 103 "KB", // 104 - "FM", // 105 *** - "OIRT", // 106 *** + "FM", // 105 + "OIRT", // 106 "Tune når skjerm av", // 107 "Velg Modell", // 108 - "Base (ILI9341)", // 109 *** + "Base (ILI9341)", // 109 "Bærbar (ILI9341)", // 110 "Bærbar touch (ILI9341)", // 111 "Titel", // 112 - "Album", // 113 *** + "Album", // 113 "Spor", // 114 - "Artist", // 115 *** + "Artist", // 115 "Komposisjon", // 116 "Bevegelse", // 117 "Dirigent", // 118 "Komponist", // 119 - "Band", // 120 *** + "Band", // 120 "Kommentar", // 121 "Sjanger", // 122 "Nyheter", // 123 "Lokale nyheter", // 124 "Aksjemarked", // 125 - "Sport", // 126 *** + "Sport", // 126 "Lotteri", // 127 "Horoskop", // 128 "Daglig avledning", // 129 "Helse", // 130 "Begivenhet", // 131 - "Scene", // 132 *** + "Scene", // 132 "Kino", // 133 - "TV", // 134 *** + "TV", // 134 "Dato/tid", // 135 "Vær", // 136 "Trafikk", // 137 - "Alarm", // 138 *** + "Alarm", // 138 "Annonse", // 139 "Nettsted", // 140 "Annet", // 141 @@ -4088,15 +4118,15 @@ static const char* const myLanguage[18][260] PROGMEM = { "Frekvens", // 149 "Hjemmeside", // 150 "Underkanal", // 151 - "Hotline", // 152 *** + "Hotline", // 152 "Studio telefon", // 153 "Telefon", // 154 - "SMS studio", // 155 *** - "SMS", // 156 *** + "SMS studio", // 155 + "SMS", // 156 "E-post hotline", // 157 "E-post studio", // 158 "E-post", // 159 - "Chat", // 160 *** + "Chat", // 160 "Chattesenter", // 161 "Stem spørsmål", // 162 "Stemmesenter", // 163 @@ -4106,13 +4136,13 @@ static const char* const myLanguage[18][260] PROGMEM = { "Kjøp", // 167 "Få data", // 168 "MB std. trinnstørrelse", // 169 - "Tangerine", // 170 *** - "Ocean", // 171 *** - "Indigo", // 172 *** + "Tangerine", // 170 + "Ocean", // 171 + "Indigo", // 172 "Batterialternativer", // 173 "Vis batterispenning", // 174 "Vis batteriprosent", // 175 - "RT buffering", // 176 *** + "RT buffering", // 176 "Hovedinnstillinger", // 177 "Lydinnstillinger", // 178 "Skjerminnstillinger", // 179 @@ -4128,12 +4158,12 @@ static const char* const myLanguage[18][260] PROGMEM = { "HOVED", // 189 "LYD", // 190 "SKJERM", // 191 - "RDS", // 192 *** - "FM", // 193 *** - "AM", // 194 *** + "RDS", // 192 + "FM", // 193 + "AM", // 194 "TILKOBLE", // 195 "DX MODUS", // 196 - "Stationlist ID", // 197 *** + "Stationlist ID", // 197 "AM AGC", // 198 "FM ettertrykkelse", // 199 "Lite", // 200 @@ -4195,7 +4225,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Folkemusikk", // 256 "Dokumentar", // 257 "Alarm test", // 258 - "Alarm!" // 259 + "Alarm!", // 259 + "Initial", // 260 + "Always" //261 }, { "Español", // Spanish @@ -4276,14 +4308,14 @@ static const char* const myLanguage[18][260] PROGMEM = { "Temporizador de\ntiempo muerto", // 75 "Apagar pantalla", // 76 "Tema", // 77 - "Skin", // 78 *** + "Skin", // 78 "Aplicaciones detectadas", // 79 "Min.", // 80 "Velocidad de SPI", // 81 "Sensibilidad\ndel escaneo", // 82 "NINGUNA", // 83 "Versión del software", // 84 - "Fundición de frecuencia", // 85 *** + "Fundición de frecuencia", // 85 "Auto", // 86 "No AF disponible", // 87 "No EON disponible", // 88 @@ -4367,7 +4399,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "Identificador", // 166 "Compra", // 167 "Obtener datos", // 168 - "Tamaño de paso\nAM predeterminado", // 169 *** + "Tamaño de paso\nAM predeterminado", // 169 "Mandarina", // 170 "Océano", // 171 "Índigo", // 172 @@ -4457,7 +4489,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Música Folk", // 256 "Documental", // 257 "Prueba de Alarma", // 258 - "¡ Alarma !" // 259 + "¡ Alarma !", // 259 + "Initial", // 260 + "Always" //261 }, { "Português", // Portuguese @@ -4545,7 +4579,7 @@ static const char* const myLanguage[18][260] PROGMEM = { "Sensibilidade\nda varredura", // 82 "NENHUM", // 83 "Versão do software", // 84 - "Fonte de frequência", // 85 *** + "Fonte de frequência", // 85 "Auto", // 86 "AF não disponível", // 87 "EON não disponível", // 88 @@ -4719,7 +4753,9 @@ static const char* const myLanguage[18][260] PROGMEM = { "Música Folk", // 256 "Documentário", // 257 "Teste de Alarme", // 258 - "Alarme – Alarme !" // 259 + "Alarme – Alarme !", // 259 + "Initial", // 260 + "Always" //261 } }; #endif From d7bf03cde88033d8803f86621bc342e18068c7c8 Mon Sep 17 00:00:00 2001 From: Sjef Verhoeven PE5PVB Date: Mon, 27 May 2024 20:30:13 +0200 Subject: [PATCH 08/13] Only parse RDS on block change --- src/TEF6686.cpp | 2295 ++++++++++++++++++++++++----------------------- src/TEF6686.h | 8 +- 2 files changed, 1153 insertions(+), 1150 deletions(-) diff --git a/src/TEF6686.cpp b/src/TEF6686.cpp index a66801f..f10b0f8 100644 --- a/src/TEF6686.cpp +++ b/src/TEF6686.cpp @@ -377,809 +377,843 @@ bool TEF6686::getStatusAM(int16_t &level, uint16_t &noise, uint16_t &cochannel, } void TEF6686::readRDS(byte showrdserrors) { - uint8_t offset; - if (rds.filter || ps_process) { - devTEF_Radio_Get_RDS_Status(&rds.rdsStat, &rds.rdsA, &rds.rdsB, &rds.rdsC, &rds.rdsD, &rds.rdsErr); - } else { - if (millis() >= rdstimer + 87) { - rdstimer += 87; + if (millis() - rdstimer > 87) { + rdstimer == millis(); + uint8_t offset; + if (rds.filter) { + devTEF_Radio_Get_RDS_Status(&rds.rdsStat, &rds.rdsA, &rds.rdsB, &rds.rdsC, &rds.rdsD, &rds.rdsErr); + } else { devTEF_Radio_Get_RDS_Data(&rds.rdsStat, &rds.rdsA, &rds.rdsB, &rds.rdsC, &rds.rdsD, &rds.rdsErr); if ((rds.rdsStat & (1 << 14))) { for (int i = 0; i < 22; i++) devTEF_Radio_Get_RDS_Data(&rds.rdsStat, &rds.rdsA, &rds.rdsB, &rds.rdsC, &rds.rdsD, &rds.rdsErr); } } - } - if (bitRead(rds.rdsStat, 9)) { - rds.hasRDS = true; // RDS decoder synchronized and data available - bitStartTime = 0; - } else { - if (bitStartTime == 0) { - bitStartTime = millis(); - } else if (millis() - bitStartTime >= 87) { - rds.hasRDS = false; - } - } - - rdsAerrorThreshold = (((rds.rdsErr >> 14) & 0x03) > showrdserrors); - rdsBerrorThreshold = (((rds.rdsErr >> 12) & 0x03) > showrdserrors); - rdsCerrorThreshold = (((rds.rdsErr >> 10) & 0x03) > showrdserrors); - rdsDerrorThreshold = (((rds.rdsErr >> 8) & 0x03) > showrdserrors); - - if (bitRead(rds.rdsStat, 9)) { // We have all data to decode... let's go... - rds.rdsAerror = (((rds.rdsErr >> 14) & 0x03) > 1); - rds.rdsBerror = (((rds.rdsErr >> 12) & 0x03) > 1); - rds.rdsCerror = (((rds.rdsErr >> 10) & 0x03) > 1); - rds.rdsDerror = (((rds.rdsErr >> 8) & 0x03) > 1); - - //PI decoder - if (!rdsAerrorThreshold && afreset) { - rds.correctPI = rds.rdsA; - afreset = false; + if (bitRead(rds.rdsStat, 9)) { + rds.hasRDS = true; // RDS decoder synchronized and data available + bitStartTime = 0; + } else { + if (bitStartTime == 0) { + bitStartTime = millis(); + } else if (millis() - bitStartTime >= 87) { + rds.hasRDS = false; + } } - if (((!rdsAerrorThreshold && !rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) || (rds.pierrors && !errorfreepi))) { - if (rds.rdsA != piold && rds.rdsA != 0) { - piold = rds.rdsA; - rds.picode[0] = (rds.rdsA >> 12) & 0xF; - rds.picode[1] = (rds.rdsA >> 8) & 0xF; - rds.picode[2] = (rds.rdsA >> 4) & 0xF; - rds.picode[3] = rds.rdsA & 0xF; - for (int i = 0; i < 4; i++) { - if (rds.picode[i] < 10) { - rds.picode[i] += '0'; // Add ASCII offset for decimal digits - } else { - rds.picode[i] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F - } - } + rdsAerrorThreshold = (((rds.rdsErr >> 14) & 0x03) > showrdserrors); + rdsBerrorThreshold = (((rds.rdsErr >> 12) & 0x03) > showrdserrors); + rdsCerrorThreshold = (((rds.rdsErr >> 10) & 0x03) > showrdserrors); + rdsDerrorThreshold = (((rds.rdsErr >> 8) & 0x03) > showrdserrors); + + if (bitRead(rds.rdsStat, 9) && (rds.rdsA != previous_rdsA || rds.rdsB != previous_rdsB || rds.rdsC != previous_rdsC || rds.rdsD != previous_rdsD)) { + rds.rdsAerror = (((rds.rdsErr >> 14) & 0x03) > 1); // We have all data to decode... let's go... + rds.rdsBerror = (((rds.rdsErr >> 12) & 0x03) > 1); + rds.rdsCerror = (((rds.rdsErr >> 10) & 0x03) > 1); + rds.rdsDerror = (((rds.rdsErr >> 8) & 0x03) > 1); + + //PI decoder + if (!rdsAerrorThreshold && afreset) { + rds.correctPI = rds.rdsA; + afreset = false; } - if (!rdsAerrorThreshold && !rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { - rds.picode[4] = ' '; - rds.picode[5] = ' '; - errorfreepi = true; - } - - if (!errorfreepi) { - if (((rds.rdsErr >> 14) & 0x03) > 2) rds.picode[5] = '?'; else rds.picode[5] = ' '; - if (((rds.rdsErr >> 14) & 0x03) > 1) rds.picode[4] = '?'; else rds.picode[4] = ' '; // Not sure, add a ? - } else { - rds.picode[4] = ' '; - rds.picode[5] = ' '; - } - rds.picode[6] = '\0'; - if (strncmp(rds.picode, "0000", 4) == 0) { - if (piold != 0) { - rds.picode[0] = (piold >> 12) & 0xF; - rds.picode[1] = (piold >> 8) & 0xF; - rds.picode[2] = (piold >> 4) & 0xF; - rds.picode[3] = piold & 0xF; + if (((!rdsAerrorThreshold && !rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) || (rds.pierrors && !errorfreepi))) { + if (rds.rdsA != piold && rds.rdsA != 0) { + piold = rds.rdsA; + rds.picode[0] = (rds.rdsA >> 12) & 0xF; + rds.picode[1] = (rds.rdsA >> 8) & 0xF; + rds.picode[2] = (rds.rdsA >> 4) & 0xF; + rds.picode[3] = rds.rdsA & 0xF; for (int i = 0; i < 4; i++) { if (rds.picode[i] < 10) { - rds.picode[i] += '0'; // Add ASCII offset for decimal digits + rds.picode[i] += '0'; // Add ASCII offset for decimal digits } else { - rds.picode[i] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F + rds.picode[i] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F } } + } + + if (!rdsAerrorThreshold && !rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { + rds.picode[4] = ' '; + rds.picode[5] = ' '; + errorfreepi = true; + } + + if (!errorfreepi) { + if (((rds.rdsErr >> 14) & 0x03) > 2) rds.picode[5] = '?'; else rds.picode[5] = ' '; + if (((rds.rdsErr >> 14) & 0x03) > 1) rds.picode[4] = '?'; else rds.picode[4] = ' '; // Not sure, add a ? } else { - if (rds.stationName.length() == 0) { + rds.picode[4] = ' '; + rds.picode[5] = ' '; + } + rds.picode[6] = '\0'; + if (strncmp(rds.picode, "0000", 4) == 0) { + if (piold != 0) { + rds.picode[0] = (piold >> 12) & 0xF; + rds.picode[1] = (piold >> 8) & 0xF; + rds.picode[2] = (piold >> 4) & 0xF; + rds.picode[3] = piold & 0xF; + for (int i = 0; i < 4; i++) { + if (rds.picode[i] < 10) { + rds.picode[i] += '0'; // Add ASCII offset for decimal digits + } else { + rds.picode[i] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F + } + } + } else { + if (rds.stationName.length() == 0) { + memset(rds.picode, 0, sizeof(rds.picode)); + } + memset(rds.picode, 0, sizeof(rds.picode)); } - - memset(rds.picode, 0, sizeof(rds.picode)); } - } - // USA Station callsign decoder - if (ps_process && rds.correctPI != 0 && rds.region == 1 && correctPIold != rds.correctPI) { - bool foundMatch = false; - File file; + // USA Station callsign decoder + if (ps_process && rds.correctPI != 0 && rds.region == 1 && correctPIold != rds.correctPI) { + bool foundMatch = false; + File file; - if (SPIFFS.begin(true)) { - delay(5); - if (currentfreq2 < 9000) { - file = SPIFFS.open("/USA_87-90.csv"); - } else if (currentfreq2 > 9000 && currentfreq2 < 9200) { - file = SPIFFS.open("/USA_90-92.csv"); - } else if (currentfreq2 > 9200 && currentfreq2 < 9400) { - file = SPIFFS.open("/USA_92-94.csv"); - } else if (currentfreq2 > 9400 && currentfreq2 < 9600) { - file = SPIFFS.open("/USA_94-96.csv"); - } else if (currentfreq2 > 9600 && currentfreq2 < 9800) { - file = SPIFFS.open("/USA_96-98.csv"); - } else if (currentfreq2 > 9800 && currentfreq2 < 10000) { - file = SPIFFS.open("/USA_98-100.csv"); - } else if (currentfreq2 > 10000 && currentfreq2 < 10200) { - file = SPIFFS.open("/USA_100-102.csv"); - } else if (currentfreq2 > 10200 && currentfreq2 < 10400) { - file = SPIFFS.open("/USA_102-104.csv"); - } else if (currentfreq2 > 10400 && currentfreq2 < 10600) { - file = SPIFFS.open("/USA_104-106.csv"); - } else if (currentfreq2 > 10600) { - file = SPIFFS.open("/USA_106-108.csv"); - } - - delay(5); - if (file) { - int i = 0; - while (file.available() && !isprint(file.peek())) { - file.read(); - i++; + if (SPIFFS.begin(true)) { + delay(5); + if (currentfreq2 < 9000) { + file = SPIFFS.open("/USA_87-90.csv"); + } else if (currentfreq2 > 9000 && currentfreq2 < 9200) { + file = SPIFFS.open("/USA_90-92.csv"); + } else if (currentfreq2 > 9200 && currentfreq2 < 9400) { + file = SPIFFS.open("/USA_92-94.csv"); + } else if (currentfreq2 > 9400 && currentfreq2 < 9600) { + file = SPIFFS.open("/USA_94-96.csv"); + } else if (currentfreq2 > 9600 && currentfreq2 < 9800) { + file = SPIFFS.open("/USA_96-98.csv"); + } else if (currentfreq2 > 9800 && currentfreq2 < 10000) { + file = SPIFFS.open("/USA_98-100.csv"); + } else if (currentfreq2 > 10000 && currentfreq2 < 10200) { + file = SPIFFS.open("/USA_100-102.csv"); + } else if (currentfreq2 > 10200 && currentfreq2 < 10400) { + file = SPIFFS.open("/USA_102-104.csv"); + } else if (currentfreq2 > 10400 && currentfreq2 < 10600) { + file = SPIFFS.open("/USA_104-106.csv"); + } else if (currentfreq2 > 10600) { + file = SPIFFS.open("/USA_106-108.csv"); } - char buffer[25]; - while (file.available()) { - int bytesRead = file.readBytesUntil('\n', buffer, sizeof(buffer) - 1); - buffer[bytesRead] = '\0'; - - char *token = strtok(buffer, ";"); - - int firstColumnValue = 0; - uint16_t frequencyValue = 0; - char stationID[8]; - char stationState[8]; - - if (token) { - firstColumnValue = atoi(token); - token = strtok(NULL, ";"); - frequencyValue = atoi(token); - token = strtok(NULL, ";"); - strncpy(stationID, token, sizeof(stationID) - 1); - stationID[sizeof(stationID) - 1] = '\0'; - token = strtok(NULL, ";"); - strncpy(stationState, token, sizeof(stationState) - 1); - stationState[sizeof(stationState) - 1] = '\0'; + delay(5); + if (file) { + int i = 0; + while (file.available() && !isprint(file.peek())) { + file.read(); + i++; } - if (frequencyValue == currentfreq2 && static_cast(firstColumnValue) == rds.correctPI) { - strncpy(rds.stationID, stationID, 7); - strncpy(rds.stationState, stationState, 2); - foundMatch = true; - break; + char buffer[25]; + while (file.available()) { + int bytesRead = file.readBytesUntil('\n', buffer, sizeof(buffer) - 1); + buffer[bytesRead] = '\0'; + + char *token = strtok(buffer, ";"); + + int firstColumnValue = 0; + uint16_t frequencyValue = 0; + char stationID[8]; + char stationState[8]; + + if (token) { + firstColumnValue = atoi(token); + token = strtok(NULL, ";"); + frequencyValue = atoi(token); + token = strtok(NULL, ";"); + strncpy(stationID, token, sizeof(stationID) - 1); + stationID[sizeof(stationID) - 1] = '\0'; + token = strtok(NULL, ";"); + strncpy(stationState, token, sizeof(stationState) - 1); + stationState[sizeof(stationState) - 1] = '\0'; + } + + if (frequencyValue == currentfreq2 && static_cast(firstColumnValue) == rds.correctPI) { + strncpy(rds.stationID, stationID, 7); + strncpy(rds.stationState, stationState, 2); + foundMatch = true; + break; + } } } } - } - if (!foundMatch) { - uint16_t stationID = rds.rdsA; - if (stationID > 4096) { - if (stationID > 21671 && (stationID & 0xF00U) >> 8 == 0) stationID = ((uint16_t)uint8_t(0xA0 + ((stationID & 0xF000U) >> 12)) << 8) + lowByte(stationID); // C0DE -> ACDE - if (stationID > 21671 && lowByte(stationID) == 0) stationID = 0xAF00 + uint8_t(highByte(stationID)); // CD00 -> AFCD - if (stationID < 39247) { - if (stationID > 21671) { - rds.stationID[0] = 'W'; - stationID -= 21672; + if (!foundMatch) { + uint16_t stationID = rds.rdsA; + if (stationID > 4096) { + if (stationID > 21671 && (stationID & 0xF00U) >> 8 == 0) stationID = ((uint16_t)uint8_t(0xA0 + ((stationID & 0xF000U) >> 12)) << 8) + lowByte(stationID); // C0DE -> ACDE + if (stationID > 21671 && lowByte(stationID) == 0) stationID = 0xAF00 + uint8_t(highByte(stationID)); // CD00 -> AFCD + if (stationID < 39247) { + if (stationID > 21671) { + rds.stationID[0] = 'W'; + stationID -= 21672; + } else { + rds.stationID[0] = 'K'; + stationID -= 4096; + } + rds.stationID[1] = char(stationID / 676 + 65); + rds.stationID[2] = char((stationID - 676 * int(stationID / 676)) / 26 + 65); + rds.stationID[3] = char(((stationID - 676 * int(stationID / 676)) % 26) + 65); } else { + stationID -= 4835; rds.stationID[0] = 'K'; - stationID -= 4096; - } - rds.stationID[1] = char(stationID / 676 + 65); - rds.stationID[2] = char((stationID - 676 * int(stationID / 676)) / 26 + 65); - rds.stationID[3] = char(((stationID - 676 * int(stationID / 676)) % 26) + 65); - } else { - stationID -= 4835; - rds.stationID[0] = 'K'; - rds.stationID[1] = char(stationID / 676 + 65); - rds.stationID[2] = char((stationID - 676 * int(stationID / 676)) / 26 + 65); - rds.stationID[3] = char(((stationID - 676 * int(stationID / 676)) % 26) + 65); - } - } - - bool faultyID = false; - - for (byte i = 0; i < 4; i++) { - if (!(rds.stationID[i] >= 'A' && rds.stationID[i] <= 'Z')) { - faultyID = true; - break; - } - } - - if (faultyID) { - strcpy(rds.stationID, "Unknown"); - } else { - rds.stationID[7] = '?'; - } - rds.stationID[8] = '\0'; - } - correctPIold = rds.correctPI; - rds.stationIDtext = rds.stationID; - rds.stationStatetext = rds.stationState; - } - } - - if (!rds.rdsBerror || showrdserrors == 3) rdsblock = rds.rdsB >> 11; else return; - switch (rdsblock) { - case RDS_GROUP_0A: - case RDS_GROUP_0B: - { - //PS decoder - if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsDerrorThreshold)) { - offset = rds.rdsB & 0x03; // Let's get the character offset for PS - - ps_buffer2[(offset * 2) + 0] = ps_buffer[(offset * 2) + 0]; // Make a copy of the PS buffer - ps_buffer2[(offset * 2) + 1] = ps_buffer[(offset * 2) + 1]; - ps_buffer2[8] = '\0'; // Endmarker - - ps_buffer[(offset * 2) + 0] = rds.rdsD >> 8; // First character of segment - ps_buffer[(offset * 2) + 1] = rds.rdsD & 0xFF; // Second character of segment - ps_buffer[8] = '\0'; // Endmarker - - if (ps_process || rds.fastps == 0) { - if (offset == 0) { - packet0 = true; - packet1 = false; - packet2 = false; - packet3 = false; - } - if (offset == 1) packet1 = true; - if (offset == 2) packet2 = true; - if (offset == 3) packet3 = true; - } - - if (packet0 && packet1 && packet2 && packet3 && (ps_process || rds.fastps == 0)) { // Last chars are received - if (strcmp(ps_buffer, ps_buffer2) == 0) { // When no difference between current and buffer, let's go... - ps_process = true; - RDScharConverter(ps_buffer2, PStext, sizeof(PStext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII - String utf8String = convertToUTF8(PStext); // Convert RDS characterset to ASCII - rds.stationName = extractUTF8Substring(utf8String, 0, 8, true); // Make sure PS does not exceed 8 characters - for (byte x = 0; x < 8; x++) { - ps_buffer[x] = '\0'; - ps_buffer2[x] = '\0'; - } + rds.stationID[1] = char(stationID / 676 + 65); + rds.stationID[2] = char((stationID - 676 * int(stationID / 676)) / 26 + 65); + rds.stationID[3] = char(((stationID - 676 * int(stationID / 676)) % 26) + 65); } } - if (!ps_process && rds.fastps > 0) { // Let's get 2 runs of 8 PS characters fast and without refresh - if (offset == 0) packet0 = true; - if (offset == 1) packet1 = true; - if (offset == 2) packet2 = true; - if (offset == 3) packet3 = true; - RDScharConverter(ps_buffer, PStext, sizeof(PStext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII - String utf8String = convertToUTF8(PStext); // Convert RDS characterset to ASCII - rds.stationName = extractUTF8Substring(utf8String, 0, 8, true); - if (packet0 && packet1 && packet2 && packet3 && rds.fastps < 2) ps_process = true;// OK, we had one runs, now let's go the idle PS writing - } + bool faultyID = false; - if (offset == 0) rds.hasDynamicPTY = bitRead(rds.rdsB, 2) & 0x1F; // Dynamic PTY flag - if (offset == 1) rds.hasCompressed = bitRead(rds.rdsB, 2) & 0x1F; // Compressed flag - if (offset == 2) rds.hasArtificialhead = bitRead(rds.rdsB, 2) & 0x1F; // Artificial head flag - if (offset == 3) rds.hasStereo = bitRead(rds.rdsB, 2) & 0x1F; // Stereo flag - } - - if (!rdsBerrorThreshold) { - rds.stationTypeCode = (rds.rdsB >> 5) & 0x1F; // Get 5 PTY bits from Block B - if (rds.region == 0) strcpy(rds.stationType, PTY_EU[rds.stationTypeCode]); - if (rds.region == 1) strcpy(rds.stationType, PTY_USA[rds.stationTypeCode]); - - rds.hasTA = (bitRead(rds.rdsB, 4)); // Read TA flag - - if ((bitRead(rds.rdsB, 3)) == 1) rds.MS = 1; else rds.MS = 2; // Read MS flag - } - - rds.hasTP = (bitRead(rds.rdsB, 10)); // Read TP flag - - if (!rdsCerrorThreshold) { - //AF decoder - if (rdsblock == 0 && rds.rdsC != rdsCold) { // Only when in GROUP 0A - - if ((rds.rdsC >> 8) > 224 && (rds.rdsC >> 8) < 250) { - afinit = true; // AF detected - rds.hasAF = true; - } - - if (afinit) { - if ((rds.rdsC >> 8) > 224 && (rds.rdsC >> 8) < 250 && ((rds.rdsC & 0xFF) * 10 + 8750) == currentfreq && rds.hasAF) { - if (afmethodBtrigger) afmethodB = true; // Check for AF method B - afmethodBprobe = true; - af_updatecounter++; - af_counterb = (rds.rdsC >> 8) - 224; - af_number = (rds.rdsC >> 8) - 224; - af_counterbcheck = 1; - doublecounter = 0; - doubletestfreq = (rds.rdsC & 0xFF) * 10 + 8750; - } else if ((rds.rdsC >> 8) > 224 && (rds.rdsC >> 8) < 250 && ((rds.rdsC & 0xFF) * 10 + 8750) != currentfreq && rds.hasAF) { - afmethodBprobe = false; - afmethodBtrigger = true; - af_counterb = 0; - af_number = (rds.rdsC >> 8) - 224; - af_counterbcheck = 0; - doublecounter = 0; - doubletestfreq = (rds.rdsC & 0xFF) * 10 + 8750; - } - - if (((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) && ((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205)) { - if (afmethodBprobe) af_counterbcheck += 2; - } - - if ((rds.rdsB >> 11) == 0 && af_counter < 50) { - uint16_t buffer0; - uint16_t buffer1; - - if ((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) buffer0 = (rds.rdsC >> 8) * 10 + 8750; else buffer0 = 0; - if ((rds.rdsC & 0xFF) > 0 && (rds.rdsC & 0xFF) < 205) buffer1 = (rds.rdsC & 0xFF) * 10 + 8750; else buffer1 = 0; - - if (((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) && (buffer0 == doubletestfreq || buffer1 == doubletestfreq)) doublecounter++; - if (doublecounter > (af_number / 2)) afmethodB = true; // If signed frequency also appears more than once in the AF list, AF Method B detected - - if (afmethodBprobe && af_counterbcheck > af_counterb) afmethodBprobe = false; // If more than counter received disable probe flag - - if (afmethodBprobe) { // Check for Reg. flags - if (buffer1 == currentfreq && buffer0 > buffer1) { - for (int x = 0; x < af_counter; x++) { - if (af[x].frequency == buffer0 && !af[x].regional) { - af[x].regional = true; - af_updatecounter++; - break; - } - } - } else if (buffer1 == currentfreq && buffer0 < buffer1) { - for (int x = 0; x < af_counter; x++) { - if (af[x].frequency == buffer0 && !af[x].same) { - af[x].same = true; - af_updatecounter++; - break; - } - } - } - - if (buffer0 == currentfreq && buffer0 > buffer1) { - for (int x = 0; x < af_counter; x++) { - if (af[x].frequency == buffer1 && !af[x].regional) { - af[x].regional = true; - af_updatecounter++; - break; - } - } - } else if (buffer0 == currentfreq && buffer0 < buffer1) { - for (int x = 0; x < af_counter; x++) { - if (af[x].frequency == buffer1 && !af[x].same) { - af[x].same = true; - af_updatecounter++; - break; - } - } - } - } - - if (buffer0 != currentfreq && buffer1 != currentfreq && afmethodB && afmethodBprobe) { - afmethodBprobe = false; // Remove faulty Reg. flags - for (int x = 0; x < af_counter; x++) { - if (af[x].frequency == buffer0 || af[x].frequency == buffer1) { - if (af[x].same) { - af[x].same = false; - af_updatecounter++; - } - if (af[x].regional) { - af[x].regional = false; - af_updatecounter++; - } - } - break; - } - } - - bool isValuePresent = false; - for (int i = 0; i < 50; i++) { // Check if already in list - if ((rds.sortaf && (buffer0 == currentfreq)) || buffer0 == 0 || af[i].frequency == buffer0) { - isValuePresent = true; - break; - } - } - - if (!isValuePresent) { // Add frequency to list - af[af_counter].frequency = buffer0; - if (af_counter < 50) af_counter++; - af_updatecounter++; - } - - isValuePresent = false; - for (int i = 0; i < 50; i++) { // Check if already in list - if ((rds.sortaf && (buffer1 == currentfreq)) || buffer1 == 0 || af[i].frequency == buffer1) { - isValuePresent = true; - break; - } - } - - if (!isValuePresent) { - af[af_counter].frequency = buffer1; // Add frequency to list - if (af_counter < 50) af_counter++; - af_updatecounter++; - } - - if (rds.sortaf) { // Sort AF list (low to high) - for (int i = 0; i < 50; i++) { - for (int j = 0; j < 50 - i; j++) { - if (af[j].frequency == 0) continue; - - if (af[j].frequency > af[j + 1].frequency && af[j + 1].frequency != 0) { - uint16_t temp = af[j].frequency; - bool temp3 = af[j].afvalid; - bool temp4 = af[j].checked; - bool temp5 = af[j].regional; - bool temp6 = af[j].same; - af[j].frequency = af[j + 1].frequency; - af[j].afvalid = af[j + 1].afvalid; - af[j].checked = af[j + 1].checked; - af[j].regional = af[j + 1].regional; - af[j].same = af[j + 1].same; - af[j + 1].frequency = temp; - af[j + 1].afvalid = temp3; - af[j + 1].checked = temp4; - af[j + 1].regional = temp5; - af[j + 1].same = temp6; - } - } - } - } - } - } - rdsCold = rds.rdsC; - } - } - } break; - - case RDS_GROUP_1A: - case RDS_GROUP_1B: { - if (!rdsCerrorThreshold) { - if (((rds.rdsC >> 12) & 0x07) == 0 && rdsblock == RDS_GROUP_1A) { // ECC code readout - rds.ECC = rds.rdsC & 0xff; - rds.hasECC = true; - - switch (rds.picode[0]) { - case '1': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[129]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[136]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[152]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[104]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[73]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[83]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[0]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[1]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[2]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[59]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[3]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[179]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[197]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[124]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[200]; - break; - } - - case '2': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[130]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[141]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[155]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[106]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[126]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[95]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[4]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[5]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[6]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[7]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[8]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[180]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[187]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[77]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[218]; - break; - } - - case '3': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[224]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[156]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[132]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[112]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[119]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[72]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[92]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[9]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[10]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[11]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[12]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[16]; - if (rds.ECC == 229) rds.ECCtext = ECCtext[63]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[181]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[227]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[189]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[219]; - break; - } - - case '4': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[148]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[157]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[127]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[69]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[91]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[103]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[13]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[14]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[15]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[182]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[210]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[213]; - break; - } - - case '5': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[133]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[159]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[68]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[79]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[88]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[78]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[17]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[18]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[19]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[65]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[183]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[191]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[193]; - break; - } - - case '6': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[134]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[163]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[171]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[96]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[105]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[123]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[125]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[20]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[21]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[22]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[24]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[184]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[76]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[201]; - break; - } - - case '7': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[139]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[161]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[164]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[113]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[81]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[84]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[25]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[26]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[27]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[62]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[185]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[207]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[221]; - break; - } - - case '8': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[142]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[170]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[116]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[117]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[74]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[82]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[28]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[29]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[30]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[31]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[32]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[186]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[195]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[212]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[190]; - break; - } - - case '9': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[143]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[162]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[174]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[120]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[102]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[107]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[33]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[34]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[35]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[36]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[37]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[80]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[209]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[196]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[211]; - if (rds.ECC == 244) rds.ECCtext = ECCtext[190]; - break; - } - - case 'A': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[131]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[144]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[166]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[85]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[97]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[121]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[70]; - if (rds.ECC == 212) rds.ECCtext = ECCtext[86]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[38]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[39]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[40]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[41]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[60]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[178]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[215]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[214]; - break; - } - - case 'B': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[137]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[145]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[167]; - if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[101]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[109]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[100]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[42]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[43]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[44]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[61]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[66]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[206]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[188]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[203]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[93]; - if (rds.ECC == 244) rds.ECCtext = ECCtext[204]; - break; - } - - case 'C': { - if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[223]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[140]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[146]; - if (rds.ECC == 165) rds.ECCtext = ECCtext[169]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[110]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[108]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[111]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[87]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[45]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[46]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[47]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[48]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[118]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[190]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[216]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[194]; - break; - } - - case 'D': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[225]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[150]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[154]; - if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[90]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[89]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[0]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[49]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[23]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[122]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[198]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[217]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[128]; - break; - } - - case 'E': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[151]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[172]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[175]; - if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[99]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[115]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[94]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[50]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[51]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[52]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[53]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[64]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[98]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[199]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[208]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[205]; - break; - } - - case 'F': { - if (rds.ECC == 161) rds.ECCtext = ECCtext[149]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[222]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[153]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[176]; - if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; - if (rds.ECC == 166) rds.ECCtext = ECCtext[168]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[67]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[75]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[114]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[54]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[55]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[56]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[57]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[58]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[202]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[192]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[220]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[71]; - break; - } - default: { - rds.ECCtext = ""; - break; - } - } - } - - if (((rds.rdsC >> 12) & 0x07) == 3 && rdsblock == RDS_GROUP_1A) { // LIC code readout - rds.LIC = rds.rdsC & 0xff; - rds.hasLIC = true; - if (rds.LIC > 0 && rds.LIC < 128) rds.LICtext = LICtext[rds.LIC]; else rds.LICtext = ""; - } - } - - if (rds.rdsC >> 12 == 1 && rdsblock == RDS_GROUP_1A) rds.hasTMC = true; // TMC flag - - if (!rdsDerrorThreshold) { - if (rds.rdsD != 0) { // PIN decoder - if ((rds.rdsD & 0x3f) < 61 && ((rds.rdsD >> 6) & 0x1f) < 24) { - rds.hasPIN = true; - rds.pinMin = rds.rdsD & 0x3f; - rds.pinHour = rds.rdsD >> 6 & 0x1f; - rds.pinDay = rds.rdsD >> 11 & 0x1f; - } - } - } - } break; - - case RDS_GROUP_2A: { - if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold)) { - // RT decoder (64 characters) - rds.hasRT = true; - rds.rtAB = (bitRead(rds.rdsB, 4)); // Get AB flag - - if (initab) { - rtABold = rds.rtAB; - initab = false; - } - - byte endmarkerRT64 = 64; - for (byte i = 0; i < endmarkerRT64; i++) { - if (rt_buffer[i] == 0x0d) { - endmarkerRT64 = i; + for (byte i = 0; i < 4; i++) { + if (!(rds.stationID[i] >= 'A' && rds.stationID[i] <= 'Z')) { + faultyID = true; break; } } - if (rds.rtAB != rtABold) { // Erase old RT, because of AB change - initrt = false; - if (rds.rtbuffer) { + if (faultyID) { + strcpy(rds.stationID, "Unknown"); + } else { + rds.stationID[7] = '?'; + } + rds.stationID[8] = '\0'; + } + correctPIold = rds.correctPI; + rds.stationIDtext = rds.stationID; + rds.stationStatetext = rds.stationState; + } + } + + if (!rds.rdsBerror || showrdserrors == 3) rdsblock = rds.rdsB >> 11; else return; + switch (rdsblock) { + case RDS_GROUP_0A: + case RDS_GROUP_0B: + { + //PS decoder + if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsDerrorThreshold)) { + offset = rds.rdsB & 0x03; // Let's get the character offset for PS + + ps_buffer2[(offset * 2) + 0] = ps_buffer[(offset * 2) + 0]; // Make a copy of the PS buffer + ps_buffer2[(offset * 2) + 1] = ps_buffer[(offset * 2) + 1]; + ps_buffer2[8] = '\0'; // Endmarker + + ps_buffer[(offset * 2) + 0] = rds.rdsD >> 8; // First character of segment + ps_buffer[(offset * 2) + 1] = rds.rdsD & 0xFF; // Second character of segment + ps_buffer[8] = '\0'; // Endmarker + + if (ps_process || rds.fastps == 0) { + if (offset == 0) { + packet0 = true; + packet1 = false; + packet2 = false; + packet3 = false; + } + if (offset == 1) packet1 = true; + if (offset == 2) packet2 = true; + if (offset == 3) packet3 = true; + } + + if (packet0 && packet1 && packet2 && packet3 && (ps_process || rds.fastps == 0)) { // Last chars are received + if (strcmp(ps_buffer, ps_buffer2) == 0) { // When no difference between current and buffer, let's go... + ps_process = true; + RDScharConverter(ps_buffer2, PStext, sizeof(PStext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII + String utf8String = convertToUTF8(PStext); // Convert RDS characterset to ASCII + rds.stationName = extractUTF8Substring(utf8String, 0, 8, true); // Make sure PS does not exceed 8 characters + for (byte x = 0; x < 8; x++) { + ps_buffer[x] = '\0'; + ps_buffer2[x] = '\0'; + } + } + } + + if (!ps_process && rds.fastps > 0) { // Let's get 2 runs of 8 PS characters fast and without refresh + if (offset == 0) packet0 = true; + if (offset == 1) packet1 = true; + if (offset == 2) packet2 = true; + if (offset == 3) packet3 = true; + RDScharConverter(ps_buffer, PStext, sizeof(PStext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII + String utf8String = convertToUTF8(PStext); // Convert RDS characterset to ASCII + rds.stationName = extractUTF8Substring(utf8String, 0, 8, true); + if (packet0 && packet1 && packet2 && packet3 && rds.fastps < 2) ps_process = true;// OK, we had one runs, now let's go the idle PS writing + } + + if (offset == 0) rds.hasDynamicPTY = bitRead(rds.rdsB, 2) & 0x1F; // Dynamic PTY flag + if (offset == 1) rds.hasCompressed = bitRead(rds.rdsB, 2) & 0x1F; // Compressed flag + if (offset == 2) rds.hasArtificialhead = bitRead(rds.rdsB, 2) & 0x1F; // Artificial head flag + if (offset == 3) rds.hasStereo = bitRead(rds.rdsB, 2) & 0x1F; // Stereo flag + } + + if (!rdsBerrorThreshold) { + rds.stationTypeCode = (rds.rdsB >> 5) & 0x1F; // Get 5 PTY bits from Block B + if (rds.region == 0) strcpy(rds.stationType, PTY_EU[rds.stationTypeCode]); + if (rds.region == 1) strcpy(rds.stationType, PTY_USA[rds.stationTypeCode]); + + rds.hasTA = (bitRead(rds.rdsB, 4)); // Read TA flag + + if ((bitRead(rds.rdsB, 3)) == 1) rds.MS = 1; else rds.MS = 2; // Read MS flag + } + + rds.hasTP = (bitRead(rds.rdsB, 10)); // Read TP flag + + if (!rdsCerrorThreshold) { + //AF decoder + if (rdsblock == 0 && rds.rdsC != rdsCold) { // Only when in GROUP 0A + + if ((rds.rdsC >> 8) > 224 && (rds.rdsC >> 8) < 250) { + afinit = true; // AF detected + rds.hasAF = true; + } + + if (afinit) { + if ((rds.rdsC >> 8) > 224 && (rds.rdsC >> 8) < 250 && ((rds.rdsC & 0xFF) * 10 + 8750) == currentfreq && rds.hasAF) { + if (afmethodBtrigger) afmethodB = true; // Check for AF method B + afmethodBprobe = true; + af_updatecounter++; + af_counterb = (rds.rdsC >> 8) - 224; + af_number = (rds.rdsC >> 8) - 224; + af_counterbcheck = 1; + doublecounter = 0; + doubletestfreq = (rds.rdsC & 0xFF) * 10 + 8750; + } else if ((rds.rdsC >> 8) > 224 && (rds.rdsC >> 8) < 250 && ((rds.rdsC & 0xFF) * 10 + 8750) != currentfreq && rds.hasAF) { + afmethodBprobe = false; + afmethodBtrigger = true; + af_counterb = 0; + af_number = (rds.rdsC >> 8) - 224; + af_counterbcheck = 0; + doublecounter = 0; + doubletestfreq = (rds.rdsC & 0xFF) * 10 + 8750; + } + + if (((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) && ((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205)) { + if (afmethodBprobe) af_counterbcheck += 2; + } + + if ((rds.rdsB >> 11) == 0 && af_counter < 50) { + uint16_t buffer0; + uint16_t buffer1; + + if ((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) buffer0 = (rds.rdsC >> 8) * 10 + 8750; else buffer0 = 0; + if ((rds.rdsC & 0xFF) > 0 && (rds.rdsC & 0xFF) < 205) buffer1 = (rds.rdsC & 0xFF) * 10 + 8750; else buffer1 = 0; + + if (((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) && (buffer0 == doubletestfreq || buffer1 == doubletestfreq)) doublecounter++; + if (doublecounter > (af_number / 2)) afmethodB = true; // If signed frequency also appears more than once in the AF list, AF Method B detected + + if (afmethodBprobe && af_counterbcheck > af_counterb) afmethodBprobe = false; // If more than counter received disable probe flag + + if (afmethodBprobe) { // Check for Reg. flags + if (buffer1 == currentfreq && buffer0 > buffer1) { + for (int x = 0; x < af_counter; x++) { + if (af[x].frequency == buffer0 && !af[x].regional) { + af[x].regional = true; + af_updatecounter++; + break; + } + } + } else if (buffer1 == currentfreq && buffer0 < buffer1) { + for (int x = 0; x < af_counter; x++) { + if (af[x].frequency == buffer0 && !af[x].same) { + af[x].same = true; + af_updatecounter++; + break; + } + } + } + + if (buffer0 == currentfreq && buffer0 > buffer1) { + for (int x = 0; x < af_counter; x++) { + if (af[x].frequency == buffer1 && !af[x].regional) { + af[x].regional = true; + af_updatecounter++; + break; + } + } + } else if (buffer0 == currentfreq && buffer0 < buffer1) { + for (int x = 0; x < af_counter; x++) { + if (af[x].frequency == buffer1 && !af[x].same) { + af[x].same = true; + af_updatecounter++; + break; + } + } + } + } + + if (buffer0 != currentfreq && buffer1 != currentfreq && afmethodB && afmethodBprobe) { + afmethodBprobe = false; // Remove faulty Reg. flags + for (int x = 0; x < af_counter; x++) { + if (af[x].frequency == buffer0 || af[x].frequency == buffer1) { + if (af[x].same) { + af[x].same = false; + af_updatecounter++; + } + if (af[x].regional) { + af[x].regional = false; + af_updatecounter++; + } + } + break; + } + } + + bool isValuePresent = false; + for (int i = 0; i < 50; i++) { // Check if already in list + if ((rds.sortaf && (buffer0 == currentfreq)) || buffer0 == 0 || af[i].frequency == buffer0) { + isValuePresent = true; + break; + } + } + + if (!isValuePresent) { // Add frequency to list + af[af_counter].frequency = buffer0; + if (af_counter < 50) af_counter++; + af_updatecounter++; + } + + isValuePresent = false; + for (int i = 0; i < 50; i++) { // Check if already in list + if ((rds.sortaf && (buffer1 == currentfreq)) || buffer1 == 0 || af[i].frequency == buffer1) { + isValuePresent = true; + break; + } + } + + if (!isValuePresent) { + af[af_counter].frequency = buffer1; // Add frequency to list + if (af_counter < 50) af_counter++; + af_updatecounter++; + } + + if (rds.sortaf) { // Sort AF list (low to high) + for (int i = 0; i < 50; i++) { + for (int j = 0; j < 50 - i; j++) { + if (af[j].frequency == 0) continue; + + if (af[j].frequency > af[j + 1].frequency && af[j + 1].frequency != 0) { + uint16_t temp = af[j].frequency; + bool temp3 = af[j].afvalid; + bool temp4 = af[j].checked; + bool temp5 = af[j].regional; + bool temp6 = af[j].same; + af[j].frequency = af[j + 1].frequency; + af[j].afvalid = af[j + 1].afvalid; + af[j].checked = af[j + 1].checked; + af[j].regional = af[j + 1].regional; + af[j].same = af[j + 1].same; + af[j + 1].frequency = temp; + af[j + 1].afvalid = temp3; + af[j + 1].checked = temp4; + af[j + 1].regional = temp5; + af[j + 1].same = temp6; + } + } + } + } + } + } + rdsCold = rds.rdsC; + } + } + } break; + + case RDS_GROUP_1A: + case RDS_GROUP_1B: { + if (!rdsCerrorThreshold) { + if (((rds.rdsC >> 12) & 0x07) == 0 && rdsblock == RDS_GROUP_1A) { // ECC code readout + rds.ECC = rds.rdsC & 0xff; + rds.hasECC = true; + + switch (rds.picode[0]) { + case '1': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[129]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[136]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[152]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[104]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[73]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[83]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[0]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[1]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[2]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[59]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[3]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[179]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[197]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[124]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[200]; + break; + } + + case '2': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[130]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[141]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[155]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[106]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[126]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[95]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[4]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[5]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[6]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[7]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[8]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[180]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[187]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[77]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[218]; + break; + } + + case '3': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[224]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[156]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[132]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[112]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[119]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[72]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[92]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[9]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[10]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[11]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[12]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[16]; + if (rds.ECC == 229) rds.ECCtext = ECCtext[63]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[181]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[227]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[189]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[219]; + break; + } + + case '4': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[148]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[157]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[127]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[69]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[91]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[103]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[13]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[14]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[15]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[182]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[210]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[213]; + break; + } + + case '5': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[133]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[159]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[68]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[79]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[88]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[78]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[17]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[18]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[19]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[65]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[183]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[191]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[193]; + break; + } + + case '6': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[134]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[163]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[171]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[96]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[105]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[123]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[125]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[20]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[21]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[22]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[24]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[184]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[76]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[201]; + break; + } + + case '7': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[139]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[161]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[164]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[113]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[81]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[84]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[25]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[26]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[27]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[62]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[185]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[207]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[221]; + break; + } + + case '8': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[142]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[170]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[116]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[117]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[74]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[82]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[28]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[29]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[30]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[31]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[32]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[186]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[195]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[212]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[190]; + break; + } + + case '9': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[143]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[162]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[174]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[120]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[102]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[107]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[33]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[34]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[35]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[36]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[37]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[80]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[209]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[196]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[211]; + if (rds.ECC == 244) rds.ECCtext = ECCtext[190]; + break; + } + + case 'A': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[131]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[144]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[166]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[85]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[97]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[121]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[70]; + if (rds.ECC == 212) rds.ECCtext = ECCtext[86]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[38]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[39]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[40]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[41]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[60]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[178]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[215]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[214]; + break; + } + + case 'B': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[137]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[145]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[167]; + if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[101]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[109]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[100]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[42]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[43]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[44]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[61]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[66]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[206]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[188]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[203]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[93]; + if (rds.ECC == 244) rds.ECCtext = ECCtext[204]; + break; + } + + case 'C': { + if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[223]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[140]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[146]; + if (rds.ECC == 165) rds.ECCtext = ECCtext[169]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[110]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[108]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[111]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[87]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[45]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[46]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[47]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[48]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[118]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[190]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[216]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[194]; + break; + } + + case 'D': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[225]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[150]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[154]; + if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[90]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[89]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[0]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[49]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[23]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[122]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[198]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[217]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[128]; + break; + } + + case 'E': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[151]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[172]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[175]; + if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[99]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[115]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[94]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[50]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[51]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[52]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[53]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[64]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[98]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[199]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[208]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[205]; + break; + } + + case 'F': { + if (rds.ECC == 161) rds.ECCtext = ECCtext[149]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[222]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[153]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[176]; + if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; + if (rds.ECC == 166) rds.ECCtext = ECCtext[168]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[67]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[75]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[114]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[54]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[55]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[56]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[57]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[58]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[202]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[192]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[220]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[71]; + break; + } + default: { + rds.ECCtext = ""; + break; + } + } + } + + if (((rds.rdsC >> 12) & 0x07) == 3 && rdsblock == RDS_GROUP_1A) { // LIC code readout + rds.LIC = rds.rdsC & 0xff; + rds.hasLIC = true; + if (rds.LIC > 0 && rds.LIC < 128) rds.LICtext = LICtext[rds.LIC]; else rds.LICtext = ""; + } + } + + if (rds.rdsC >> 12 == 1 && rdsblock == RDS_GROUP_1A) rds.hasTMC = true; // TMC flag + + if (!rdsDerrorThreshold) { + if (rds.rdsD != 0) { // PIN decoder + if ((rds.rdsD & 0x3f) < 61 && ((rds.rdsD >> 6) & 0x1f) < 24) { + rds.hasPIN = true; + rds.pinMin = rds.rdsD & 0x3f; + rds.pinHour = rds.rdsD >> 6 & 0x1f; + rds.pinDay = rds.rdsD >> 11 & 0x1f; + } + } + } + } break; + + case RDS_GROUP_2A: { + if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold)) { + // RT decoder (64 characters) + rds.hasRT = true; + rds.rtAB = (bitRead(rds.rdsB, 4)); // Get AB flag + + if (initab) { + rtABold = rds.rtAB; + initab = false; + } + + byte endmarkerRT64 = 64; + for (byte i = 0; i < endmarkerRT64; i++) { + if (rt_buffer[i] == 0x0d) { + endmarkerRT64 = i; + break; + } + } + + if (rds.rtAB != rtABold) { // Erase old RT, because of AB change + initrt = false; + if (rds.rtbuffer) { + char rt_buffer_temp[129]; + bool found = false; + strcpy(rt_buffer_temp, rt_buffer); + + for (int i = 0; i < 129; i++) { + if (rt_buffer_temp[i] == 0x0D) { + found = true; + } + if (found) { + rt_buffer_temp[i] = '\0'; + } + } + + wchar_t RTtext[65] = L""; // Create 16 bit char buffer for Extended ASCII + RDScharConverter(rt_buffer_temp, RTtext, sizeof(RTtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII + rds.stationText = convertToUTF8(RTtext); // Convert RDS characterset to ASCII + rds.stationText = extractUTF8Substring(rds.stationText, 0, endmarkerRT64, true); // Make sure RT does not exceed 64 characters + rds.stationText = trimTrailingSpaces(rds.stationText); // Trim empty spaces at the end + } + + for (byte i = 0; i < 64; i++) { + rt_buffer[i] = 0x20; + } + rt_buffer[64] = '\0'; + rtABold = rds.rtAB; + } + + offset = (rds.rdsB & 0xf) * 4; // Get RT character segment + rt_buffer[offset + 0] = rds.rdsC >> 8; // First character of segment + rt_buffer[offset + 1] = rds.rdsC & 0xff; // Second character of segment + rt_buffer[offset + 2] = rds.rdsD >> 8; // Thirth character of segment + rt_buffer[offset + 3] = rds.rdsD & 0xff; // Fourth character of segment + + + if (initrt || !rds.rtbuffer) { char rt_buffer_temp[129]; bool found = false; strcpy(rt_buffer_temp, rt_buffer); @@ -1200,24 +1234,39 @@ void TEF6686::readRDS(byte showrdserrors) { rds.stationText = trimTrailingSpaces(rds.stationText); // Trim empty spaces at the end } - for (byte i = 0; i < 64; i++) { - rt_buffer[i] = 0x20; - } - rt_buffer[64] = '\0'; - rtABold = rds.rtAB; + for (int i = 0; i < 64; i++) rt_buffer2[i] = rt_buffer[i]; } + } break; - offset = (rds.rdsB & 0xf) * 4; // Get RT character segment - rt_buffer[offset + 0] = rds.rdsC >> 8; // First character of segment - rt_buffer[offset + 1] = rds.rdsC & 0xff; // Second character of segment - rt_buffer[offset + 2] = rds.rdsD >> 8; // Thirth character of segment - rt_buffer[offset + 3] = rds.rdsD & 0xff; // Fourth character of segment + case RDS_GROUP_2B: { + if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsDerrorThreshold)) { + // RT decoder (32 characters) + rds.hasRT = true; + rds.rtAB32 = (bitRead(rds.rdsB, 4)); // Get AB flag + if (rds.rtAB32 != rtAB32old) { // Erase old RT, because of AB change + for (byte i = 0; i < 33; i++) { + rt_buffer32[i] = 0x20; + } + rt_buffer32[32] = '\0'; + rtAB32old = rds.rtAB32; + } + + offset = (rds.rdsB & 0xf) * 2; // Get RT character segment + rt_buffer32[offset + 0] = rds.rdsD >> 8; // First character of segment + rt_buffer32[offset + 1] = rds.rdsD & 0xff; // Second character of segment + + byte endmarkerRT32 = 32; + for (byte i = 0; i < endmarkerRT32; i++) { + if (rt_buffer[i] == 0x0d) { + endmarkerRT32 = i; + break; + } + } - if (initrt || !rds.rtbuffer) { char rt_buffer_temp[129]; bool found = false; - strcpy(rt_buffer_temp, rt_buffer); + strcpy(rt_buffer_temp, rt_buffer32); for (int i = 0; i < 129; i++) { if (rt_buffer_temp[i] == 0x0D) { @@ -1228,417 +1277,373 @@ void TEF6686::readRDS(byte showrdserrors) { } } - wchar_t RTtext[65] = L""; // Create 16 bit char buffer for Extended ASCII - RDScharConverter(rt_buffer_temp, RTtext, sizeof(RTtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII - rds.stationText = convertToUTF8(RTtext); // Convert RDS characterset to ASCII - rds.stationText = extractUTF8Substring(rds.stationText, 0, endmarkerRT64, true); // Make sure RT does not exceed 64 characters - rds.stationText = trimTrailingSpaces(rds.stationText); // Trim empty spaces at the end + wchar_t RTtext[33] = L""; // Create 16 bit char buffer for Extended ASCII + RDScharConverter(rt_buffer_temp, RTtext, sizeof(RTtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII + rds.stationText32 = convertToUTF8(RTtext); // Convert RDS characterset to ASCII + rds.stationText32 = extractUTF8Substring(rds.stationText32, 0, endmarkerRT32, true);// Make sure RT does not exceed 32 characters + rds.stationText = trimTrailingSpaces(rds.stationText); // Trim empty spaces at the end } + } break; - for (int i = 0; i < 64; i++) rt_buffer2[i] = rt_buffer[i]; - } - } break; + case RDS_GROUP_3A: { + if (!rdsDerrorThreshold) { + if (rds.rdsD != 0) rds.hasAID = true; - case RDS_GROUP_2B: { - if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsDerrorThreshold)) { - // RT decoder (32 characters) - rds.hasRT = true; - rds.rtAB32 = (bitRead(rds.rdsB, 4)); // Get AB flag - - if (rds.rtAB32 != rtAB32old) { // Erase old RT, because of AB change - for (byte i = 0; i < 33; i++) { - rt_buffer32[i] = 0x20; + bool isValuePresent = false; + for (int i = 0; i < 10; i++) { + if (rds.aid[i] == rds.rdsD) { + isValuePresent = true; + break; + } } - rt_buffer32[32] = '\0'; - rtAB32old = rds.rtAB32; + + if (!isValuePresent) { + rds.aid[rds.aid_counter] = rds.rdsD; + rds.aid_counter++; + } + + if (rds.rdsD == 0xCD46) { // Check for TMC application + rds.hasTMC = true; // TMC flag + } + + // RT+ init + if (rds.rdsD == 0x4BD7) { // Check for RT+ application + rds.hasRDSplus = true; // Set flag + rtplusblock = ((rds.rdsB & 0x1F) >> 1) * 2; // Get RT+ Block + } + + if (rds.rdsD == 0x0093) { // Check for DAB+ AF application + rds.hasDABAF = true; // Set flag + DABAFblock = ((rds.rdsB & 0x1F) >> 1) * 2; // Get DAB AF Block + } + + if (rds.rdsD == 0x6552) { // Check for Enhanced RT application + _hasEnhancedRT = true; // Set flag + eRTblock = ((rds.rdsB & 0x1F) >> 1) * 2; // Get eRT block + eRTcoding = bitRead(rds.rdsC, 0); // 0 = UCS-2, 1 = UTF-8 + } + } + } break; + + case RDS_GROUP_4A: { + if (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { + // CT + uint32_t mjd; + mjd = (rds.rdsB & 0x03); + mjd <<= 15; + mjd += ((rds.rdsC >> 1) & 0x7FFF); + + long J, C, Y, M; + J = mjd + 2400001 + 68569; + C = 4 * J / 146097; + J = J - (146097 * C + 3) / 4; + Y = 4000 * (J + 1) / 1461001; + J = J - 1461 * Y / 4 + 31; + M = 80 * (J + 0) / 2447; + + if ((J - 2447 * M / 80) < 32) rds.day = J - 2447 * M / 80; + J = M / 11; + + if ((M + 2 - (12 * J)) < 13) rds.month = M + 2 - (12 * J); + if ((100 * (C - 49) + Y + J) > 2022) rds.year = 100 * (C - 49) + Y + J; + if ((((rds.rdsD >> 12) & 0x0f)) < 25) { + rds.hour = ((rds.rdsD >> 12) & 0x0f); + rds.hour += ((rds.rdsC << 4) & 0x0010); + rds.offset = ((bitRead(rds.rdsD, 5) ? -rds.rdsD & 0x3f : rds.rdsD & 0x3f) / 2); + if (bitRead(rds.rdsD, 5) & 0x3f) rds.hour -= rds.offset; else rds.hour += rds.offset; + rds.hour = (((byte)rds.hour + 24) % 24); + } + if (((rds.rdsD >> 6) & 0x3f) < 60) rds.minute = (rds.rdsD >> 6) & 0x3f; + rds.hasCT = true; + } + } break; + + case RDS_GROUP_10A: { + if (!rdsCerrorThreshold && !rdsDerrorThreshold) { + // PTYN + offset = bitRead(rds.rdsB, 0); // Get char offset + if (rds.rdsC != 0 && rds.rdsD != 0) { + ptyn_buffer[(offset * 4) + 0] = rds.rdsC >> 8; // Get position 1 and 5 + ptyn_buffer[(offset * 4) + 1] = rds.rdsC & 0xFF; // Get position 2 and 6 + ptyn_buffer[(offset * 4) + 2] = rds.rdsD >> 8; // Get position 3 and 7 + ptyn_buffer[(offset * 4) + 3] = rds.rdsD & 0xFF; // Get position 4 and 8 + for (byte i = 0; i < 8; i++) PTYNtext[i] = L'\0'; + RDScharConverter(ptyn_buffer, PTYNtext, sizeof(PTYNtext) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII + String utf8String = convertToUTF8(PTYNtext); // Convert RDS characterset to ASCII + rds.PTYN = extractUTF8Substring(utf8String, 0, 8, false); // Make sure text is not longer than 8 chars + rds.hasPTYN = true; + } + } + } break; + + case RDS_GROUP_5A: + case RDS_GROUP_6A: + case RDS_GROUP_7A: + case RDS_GROUP_8A: + case RDS_GROUP_9A: + case RDS_GROUP_11A: + case RDS_GROUP_12A: + case RDS_GROUP_13A: { + // RT+ decoding + if ((!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) && rtplusblock == rdsblock && rds.hasRDSplus) { + rds.rdsplusTag1 = ((rds.rdsB & 0x07) << 3) + (rds.rdsC >> 13); // Are we in the right RT+ block and is all ok to go? + rds.rdsplusTag2 = ((rds.rdsC & 0x01) << 5) + (rds.rdsD >> 11); + uint16_t start_marker_1 = (rds.rdsC >> 7) & 0x3F; + uint16_t length_marker_1 = (rds.rdsC >> 1) & 0x3F; + uint16_t start_marker_2 = (rds.rdsD >> 5) & 0x3F; + uint16_t length_marker_2 = (rds.rdsD & 0x1F); + togglebit = bitRead(lowByte(rds.rdsB), 4); + runningbit = bitRead(lowByte(rds.rdsB), 3); + + switch (rds.rdsplusTag1) { + case 0: rds.rdsplusTag1 = 169; break; + case 1 ... 53: rds.rdsplusTag1 += 111; break; + case 59 ... 63: rds.rdsplusTag1 += 105; break; + default: rds.rdsplusTag1 = 169; break; + } + + switch (rds.rdsplusTag2) { + case 0: rds.rdsplusTag2 = 169; break; + case 1 ... 53: rds.rdsplusTag2 += 111; break; + case 59 ... 63: rds.rdsplusTag2 += 105; break; + default: rds.rdsplusTag2 = 169; break; + } + + if (togglebit) { + for (int i = 0; i < 44; i++) { + RDSplus1[i] = 0x20; + RDSplus2[i] = 0x20; + } + RDSplus1[44] = 0; + RDSplus2[44] = 0; + } + + if (rds.rtAB == rtABold) { + for (int i = 0; i <= length_marker_1; i++)RDSplus1[i] = rt_buffer2[i + start_marker_1]; + RDSplus1[length_marker_1 + 1] = 0; + + for (int i = 0; i <= length_marker_2; i++)RDSplus2[i] = rt_buffer2[i + start_marker_2]; + RDSplus2[length_marker_2 + 1] = 0; + } + + wchar_t RTtext1[45] = L""; // Create 16 bit char buffer for Extended ASCII + RDScharConverter(RDSplus1, RTtext1, sizeof(RTtext1) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII + rds.RTContent1 = convertToUTF8(RTtext1); // Convert RDS characterset to ASCII + rds.RTContent1 = extractUTF8Substring(rds.RTContent1, 0, 44, false); // Make sure RT does not exceed 32 characters + + wchar_t RTtext2[45] = L""; // Create 16 bit char buffer for Extended ASCII + RDScharConverter(RDSplus2, RTtext2, sizeof(RTtext2) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII + rds.RTContent2 = convertToUTF8(RTtext2); // Convert RDS characterset to ASCII + rds.RTContent2 = extractUTF8Substring(rds.RTContent2, 0, 44, false); // Make sure RT does not exceed 32 characters } - offset = (rds.rdsB & 0xf) * 2; // Get RT character segment - rt_buffer32[offset + 0] = rds.rdsD >> 8; // First character of segment - rt_buffer32[offset + 1] = rds.rdsD & 0xff; // Second character of segment + // eRT decoding + if ((!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) && eRTblock == rdsblock && _hasEnhancedRT) { + offset = (rds.rdsB & 0x1f) * 4; + eRT_buffer[offset + 0] = rds.rdsC >> 8; // First character of segment + eRT_buffer[offset + 1] = rds.rdsC & 0xff; // Second character of segment + eRT_buffer[offset + 2] = rds.rdsD >> 8; // Thirth character of segment + eRT_buffer[offset + 3] = rds.rdsD & 0xff; // Fourth character of segment + eRT_buffer[127] = '\0'; - byte endmarkerRT32 = 32; - for (byte i = 0; i < endmarkerRT32; i++) { - if (rt_buffer[i] == 0x0d) { - endmarkerRT32 = i; - break; + byte endmarkereRT = 127; + bool foundendmarker = false; + for (byte i = 0; i < endmarkereRT; i++) { + if (eRT_buffer[i] == 0x0d) { + foundendmarker = true; + endmarkereRT = i; + break; + } + } + + if (offset == 0 || foundendmarker) { + if (eRTcoding) rds.enhancedRTtext = eRT_buffer; else rds.enhancedRTtext = ucs2ToUtf8(eRT_buffer); // Convert to UTF-8 or UCS-2 + rds.enhancedRTtext = trimTrailingSpaces(rds.enhancedRTtext); + if (rds.enhancedRTtext.length() > 0) rds.hasEnhancedRT = true; } } - char rt_buffer_temp[129]; - bool found = false; - strcpy(rt_buffer_temp, rt_buffer32); - for (int i = 0; i < 129; i++) { - if (rt_buffer_temp[i] == 0x0D) { - found = true; + if (!rdsBerrorThreshold && rdsblock == 16 && (bitRead(rds.rdsB, 15))) rds.hasTMC = true; // TMC flag + + if ((!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) && DABAFblock == rdsblock && rds.hasDABAF) { + rds.dabaffreq = (rds.rdsC * 16); + + for (size_t i = 0; i < sizeof(DABfrequencyTable) / sizeof(DABfrequencyTable[0]); ++i) { + if (DABfrequencyTable[i].frequency == rds.dabaffreq) strcpy(rds.dabafchannel, DABfrequencyTable[i].label); } - if (found) { - rt_buffer_temp[i] = '\0'; - } - } - wchar_t RTtext[33] = L""; // Create 16 bit char buffer for Extended ASCII - RDScharConverter(rt_buffer_temp, RTtext, sizeof(RTtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII - rds.stationText32 = convertToUTF8(RTtext); // Convert RDS characterset to ASCII - rds.stationText32 = extractUTF8Substring(rds.stationText32, 0, endmarkerRT32, true);// Make sure RT does not exceed 32 characters - rds.stationText = trimTrailingSpaces(rds.stationText); // Trim empty spaces at the end - } - } break; - - case RDS_GROUP_3A: { - if (!rdsDerrorThreshold) { - if (rds.rdsD != 0) rds.hasAID = true; - - bool isValuePresent = false; - for (int i = 0; i < 10; i++) { - if (rds.aid[i] == rds.rdsD) { - isValuePresent = true; - break; - } - } - - if (!isValuePresent) { - rds.aid[rds.aid_counter] = rds.rdsD; - rds.aid_counter++; - } - - if (rds.rdsD == 0xCD46) { // Check for TMC application - rds.hasTMC = true; // TMC flag - } - - // RT+ init - if (rds.rdsD == 0x4BD7) { // Check for RT+ application - rds.hasRDSplus = true; // Set flag - rtplusblock = ((rds.rdsB & 0x1F) >> 1) * 2; // Get RT+ Block - } - - if (rds.rdsD == 0x0093) { // Check for DAB+ AF application - rds.hasDABAF = true; // Set flag - DABAFblock = ((rds.rdsB & 0x1F) >> 1) * 2; // Get DAB AF Block - } - - if (rds.rdsD == 0x6552) { // Check for Enhanced RT application - _hasEnhancedRT = true; // Set flag - eRTblock = ((rds.rdsB & 0x1F) >> 1) * 2; // Get eRT block - eRTcoding = bitRead(rds.rdsC, 0); // 0 = UCS-2, 1 = UTF-8 - } - } - } break; - - case RDS_GROUP_4A: { - if (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { - // CT - uint32_t mjd; - mjd = (rds.rdsB & 0x03); - mjd <<= 15; - mjd += ((rds.rdsC >> 1) & 0x7FFF); - - long J, C, Y, M; - J = mjd + 2400001 + 68569; - C = 4 * J / 146097; - J = J - (146097 * C + 3) / 4; - Y = 4000 * (J + 1) / 1461001; - J = J - 1461 * Y / 4 + 31; - M = 80 * (J + 0) / 2447; - - if ((J - 2447 * M / 80) < 32) rds.day = J - 2447 * M / 80; - J = M / 11; - - if ((M + 2 - (12 * J)) < 13) rds.month = M + 2 - (12 * J); - if ((100 * (C - 49) + Y + J) > 2022) rds.year = 100 * (C - 49) + Y + J; - if ((((rds.rdsD >> 12) & 0x0f)) < 25) { - rds.hour = ((rds.rdsD >> 12) & 0x0f); - rds.hour += ((rds.rdsC << 4) & 0x0010); - rds.offset = ((bitRead(rds.rdsD, 5) ? -rds.rdsD & 0x3f : rds.rdsD & 0x3f) / 2); - if (bitRead(rds.rdsD, 5) & 0x3f) rds.hour -= rds.offset; else rds.hour += rds.offset; - rds.hour = (((byte)rds.hour + 24) % 24); - } - if (((rds.rdsD >> 6) & 0x3f) < 60) rds.minute = (rds.rdsD >> 6) & 0x3f; - rds.hasCT = true; - } - } break; - - case RDS_GROUP_10A: { - if (!rdsCerrorThreshold && !rdsDerrorThreshold) { - // PTYN - offset = bitRead(rds.rdsB, 0); // Get char offset - if (rds.rdsC != 0 && rds.rdsD != 0) { - ptyn_buffer[(offset * 4) + 0] = rds.rdsC >> 8; // Get position 1 and 5 - ptyn_buffer[(offset * 4) + 1] = rds.rdsC & 0xFF; // Get position 2 and 6 - ptyn_buffer[(offset * 4) + 2] = rds.rdsD >> 8; // Get position 3 and 7 - ptyn_buffer[(offset * 4) + 3] = rds.rdsD & 0xFF; // Get position 4 and 8 - for (byte i = 0; i < 8; i++) PTYNtext[i] = L'\0'; - RDScharConverter(ptyn_buffer, PTYNtext, sizeof(PTYNtext) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII - String utf8String = convertToUTF8(PTYNtext); // Convert RDS characterset to ASCII - rds.PTYN = extractUTF8Substring(utf8String, 0, 8, false); // Make sure text is not longer than 8 chars - rds.hasPTYN = true; - } - } - } break; - - case RDS_GROUP_5A: - case RDS_GROUP_6A: - case RDS_GROUP_7A: - case RDS_GROUP_8A: - case RDS_GROUP_9A: - case RDS_GROUP_11A: - case RDS_GROUP_12A: - case RDS_GROUP_13A: { - // RT+ decoding - if ((!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) && rtplusblock == rdsblock && rds.hasRDSplus) { - rds.rdsplusTag1 = ((rds.rdsB & 0x07) << 3) + (rds.rdsC >> 13); // Are we in the right RT+ block and is all ok to go? - rds.rdsplusTag2 = ((rds.rdsC & 0x01) << 5) + (rds.rdsD >> 11); - uint16_t start_marker_1 = (rds.rdsC >> 7) & 0x3F; - uint16_t length_marker_1 = (rds.rdsC >> 1) & 0x3F; - uint16_t start_marker_2 = (rds.rdsD >> 5) & 0x3F; - uint16_t length_marker_2 = (rds.rdsD & 0x1F); - togglebit = bitRead(lowByte(rds.rdsB), 4); - runningbit = bitRead(lowByte(rds.rdsB), 3); - - switch (rds.rdsplusTag1) { - case 0: rds.rdsplusTag1 = 169; break; - case 1 ... 53: rds.rdsplusTag1 += 111; break; - case 59 ... 63: rds.rdsplusTag1 += 105; break; - default: rds.rdsplusTag1 = 169; break; - } - - switch (rds.rdsplusTag2) { - case 0: rds.rdsplusTag2 = 169; break; - case 1 ... 53: rds.rdsplusTag2 += 111; break; - case 59 ... 63: rds.rdsplusTag2 += 105; break; - default: rds.rdsplusTag2 = 169; break; - } - - if (togglebit) { - for (int i = 0; i < 44; i++) { - RDSplus1[i] = 0x20; - RDSplus2[i] = 0x20; - } - RDSplus1[44] = 0; - RDSplus2[44] = 0; - } - - if (rds.rtAB == rtABold) { - for (int i = 0; i <= length_marker_1; i++)RDSplus1[i] = rt_buffer2[i + start_marker_1]; - RDSplus1[length_marker_1 + 1] = 0; - - for (int i = 0; i <= length_marker_2; i++)RDSplus2[i] = rt_buffer2[i + start_marker_2]; - RDSplus2[length_marker_2 + 1] = 0; - } - - wchar_t RTtext1[45] = L""; // Create 16 bit char buffer for Extended ASCII - RDScharConverter(RDSplus1, RTtext1, sizeof(RTtext1) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII - rds.RTContent1 = convertToUTF8(RTtext1); // Convert RDS characterset to ASCII - rds.RTContent1 = extractUTF8Substring(rds.RTContent1, 0, 44, false); // Make sure RT does not exceed 32 characters - - wchar_t RTtext2[45] = L""; // Create 16 bit char buffer for Extended ASCII - RDScharConverter(RDSplus2, RTtext2, sizeof(RTtext2) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII - rds.RTContent2 = convertToUTF8(RTtext2); // Convert RDS characterset to ASCII - rds.RTContent2 = extractUTF8Substring(rds.RTContent2, 0, 44, false); // Make sure RT does not exceed 32 characters - } - - // eRT decoding - if ((!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) && eRTblock == rdsblock && _hasEnhancedRT) { - offset = (rds.rdsB & 0x1f) * 4; - eRT_buffer[offset + 0] = rds.rdsC >> 8; // First character of segment - eRT_buffer[offset + 1] = rds.rdsC & 0xff; // Second character of segment - eRT_buffer[offset + 2] = rds.rdsD >> 8; // Thirth character of segment - eRT_buffer[offset + 3] = rds.rdsD & 0xff; // Fourth character of segment - eRT_buffer[127] = '\0'; - - byte endmarkereRT = 127; - bool foundendmarker = false; - for (byte i = 0; i < endmarkereRT; i++) { - if (eRT_buffer[i] == 0x0d) { - foundendmarker = true; - endmarkereRT = i; - break; - } - } - - if (offset == 0 || foundendmarker) { - if (eRTcoding) rds.enhancedRTtext = eRT_buffer; else rds.enhancedRTtext = ucs2ToUtf8(eRT_buffer); // Convert to UTF-8 or UCS-2 - rds.enhancedRTtext = trimTrailingSpaces(rds.enhancedRTtext); - if (rds.enhancedRTtext.length() > 0) rds.hasEnhancedRT = true; - } - } - - - if (!rdsBerrorThreshold && rdsblock == 16 && (bitRead(rds.rdsB, 15))) rds.hasTMC = true; // TMC flag - - if ((!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) && DABAFblock == rdsblock && rds.hasDABAF) { - rds.dabaffreq = (rds.rdsC * 16); - - for (size_t i = 0; i < sizeof(DABfrequencyTable) / sizeof(DABfrequencyTable[0]); ++i) { - if (DABfrequencyTable[i].frequency == rds.dabaffreq) strcpy(rds.dabafchannel, DABfrequencyTable[i].label); - } - - rds.dabafeid[0] = (rds.rdsD >> 12) & 0xF; - rds.dabafeid[1] = (rds.rdsD >> 8) & 0xF; - rds.dabafeid[2] = (rds.rdsD >> 4) & 0xF; - rds.dabafeid[3] = rds.rdsD & 0xF; - for (int i = 0; i < 4; i++) { - if (rds.dabafeid[i] < 10) { - rds.dabafeid[i] += '0'; // Add ASCII offset for decimal digits - } else { - rds.dabafeid[i] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F - } - } - rds.dabafeid[4] = 0; - } - } - break; - - case RDS_GROUP_14A: { - // EON - if (!rdsAerrorThreshold && !rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { - rds.hasEON = true; // Group is there, so we have EON - - bool isValuePresent = false; - int eonIndex = -1; - int i = 0; - for (; i < 20; i++) { - if (eon[i].pi == rds.rdsD || rds.rdsA == rds.rdsD) { // Check if EON is already in array - isValuePresent = true; - eonIndex = i; - break; - } - } - - if (!isValuePresent) { - eon[eon_counter].picode[0] = (rds.rdsD >> 12) & 0xF; - eon[eon_counter].picode[1] = (rds.rdsD >> 8) & 0xF; - eon[eon_counter].picode[2] = (rds.rdsD >> 4) & 0xF; - eon[eon_counter].picode[3] = rds.rdsD & 0xF; - for (int j = 0; j < 4; j++) { - if (eon[eon_counter].picode[j] < 10) { - eon[eon_counter].picode[j] += '0'; // Add ASCII offset for decimal digits + rds.dabafeid[0] = (rds.rdsD >> 12) & 0xF; + rds.dabafeid[1] = (rds.rdsD >> 8) & 0xF; + rds.dabafeid[2] = (rds.rdsD >> 4) & 0xF; + rds.dabafeid[3] = rds.rdsD & 0xF; + for (int i = 0; i < 4; i++) { + if (rds.dabafeid[i] < 10) { + rds.dabafeid[i] += '0'; // Add ASCII offset for decimal digits } else { - eon[eon_counter].picode[j] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F + rds.dabafeid[i] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F + } + } + rds.dabafeid[4] = 0; + } + } + break; + + case RDS_GROUP_14A: { + // EON + if (!rdsAerrorThreshold && !rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { + rds.hasEON = true; // Group is there, so we have EON + + bool isValuePresent = false; + int eonIndex = -1; + int i = 0; + for (; i < 20; i++) { + if (eon[i].pi == rds.rdsD || rds.rdsA == rds.rdsD) { // Check if EON is already in array + isValuePresent = true; + eonIndex = i; + break; } } - eon[eon_counter].pi = rds.rdsD; // Store PI on next array - if (eon_counter < 20) eon_counter++; - eonIndex = eon_counter - 1; - } else { - eonIndex = i; - } + if (!isValuePresent) { + eon[eon_counter].picode[0] = (rds.rdsD >> 12) & 0xF; + eon[eon_counter].picode[1] = (rds.rdsD >> 8) & 0xF; + eon[eon_counter].picode[2] = (rds.rdsD >> 4) & 0xF; + eon[eon_counter].picode[3] = rds.rdsD & 0xF; + for (int j = 0; j < 4; j++) { + if (eon[eon_counter].picode[j] < 10) { + eon[eon_counter].picode[j] += '0'; // Add ASCII offset for decimal digits + } else { + eon[eon_counter].picode[j] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F + } + } - offset = rds.rdsB & 0x0F; // Read offset + eon[eon_counter].pi = rds.rdsD; // Store PI on next array + if (eon_counter < 20) eon_counter++; + eonIndex = eon_counter - 1; + } else { + eonIndex = i; + } - if (offset < 4 && eon[eonIndex].pi == rds.rdsD) { - eon_buffer[eonIndex][(offset * 2) + 0] = rds.rdsC >> 8; // First character of segment - eon_buffer[eonIndex][(offset * 2) + 1] = rds.rdsC & 0xFF; // Second character of segment - } + offset = rds.rdsB & 0x0F; // Read offset - if (eon[eonIndex].pi == rds.rdsD) { // Last chars are received - RDScharConverter(eon_buffer[eonIndex], EONPStext[eonIndex], sizeof(EONPStext[eonIndex]) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII - String utf8String = convertToUTF8(EONPStext[eonIndex]); // Convert RDS characterset to ASCII - eon[eonIndex].ps = extractUTF8Substring(utf8String, 0, 8, false); // Make sure PS does not exceed 8 characters - } + if (offset < 4 && eon[eonIndex].pi == rds.rdsD) { + eon_buffer[eonIndex][(offset * 2) + 0] = rds.rdsC >> 8; // First character of segment + eon_buffer[eonIndex][(offset * 2) + 1] = rds.rdsC & 0xFF; // Second character of segment + } - if (offset == 13 && eon[eonIndex].pi == rds.rdsD) { - eon[eonIndex].taset = true; - eon[eonIndex].ta = bitRead(rds.rdsC, 0); - eon[eonIndex].pty = (rds.rdsC >> 11) & 0xF; - } + if (eon[eonIndex].pi == rds.rdsD) { // Last chars are received + RDScharConverter(eon_buffer[eonIndex], EONPStext[eonIndex], sizeof(EONPStext[eonIndex]) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII + String utf8String = convertToUTF8(EONPStext[eonIndex]); // Convert RDS characterset to ASCII + eon[eonIndex].ps = extractUTF8Substring(utf8String, 0, 8, false); // Make sure PS does not exceed 8 characters + } - if (bitRead(rds.rdsB, 4) && eon[eonIndex].pi == rds.rdsD) eon[eonIndex].tp = true; + if (offset == 13 && eon[eonIndex].pi == rds.rdsD) { + eon[eonIndex].taset = true; + eon[eonIndex].ta = bitRead(rds.rdsC, 0); + eon[eonIndex].pty = (rds.rdsC >> 11) & 0xF; + } - if (offset > 4 && offset < 9 && eon[eonIndex].pi == rds.rdsD) { // Check if mapped frequency belongs to current frequency - if (((rds.rdsC >> 8) * 10 + 8750) == currentfreq && (rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) { - if (eon[eonIndex].mappedfreq == 0) { - eon[eonIndex].mappedfreq = ((rds.rdsC & 0xFF) * 10 + 8750); // Add mapped frequency to array - } else if (eon[eonIndex].mappedfreq2 == 0 && eon[eonIndex].mappedfreq != ((rds.rdsC & 0xFF) * 10 + 8750) && (rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) { - eon[eonIndex].mappedfreq2 = ((rds.rdsC & 0xFF) * 10 + 8750); - } else if (eon[eonIndex].mappedfreq3 == 0 && eon[eonIndex].mappedfreq2 != ((rds.rdsC & 0xFF) * 10 + 8750) && eon[eonIndex].mappedfreq != ((rds.rdsC & 0xFF) * 10 + 8750) && (rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) { - eon[eonIndex].mappedfreq3 = ((rds.rdsC & 0xFF) * 10 + 8750); + if (bitRead(rds.rdsB, 4) && eon[eonIndex].pi == rds.rdsD) eon[eonIndex].tp = true; + + if (offset > 4 && offset < 9 && eon[eonIndex].pi == rds.rdsD) { // Check if mapped frequency belongs to current frequency + if (((rds.rdsC >> 8) * 10 + 8750) == currentfreq && (rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) { + if (eon[eonIndex].mappedfreq == 0) { + eon[eonIndex].mappedfreq = ((rds.rdsC & 0xFF) * 10 + 8750); // Add mapped frequency to array + } else if (eon[eonIndex].mappedfreq2 == 0 && eon[eonIndex].mappedfreq != ((rds.rdsC & 0xFF) * 10 + 8750) && (rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) { + eon[eonIndex].mappedfreq2 = ((rds.rdsC & 0xFF) * 10 + 8750); + } else if (eon[eonIndex].mappedfreq3 == 0 && eon[eonIndex].mappedfreq2 != ((rds.rdsC & 0xFF) * 10 + 8750) && eon[eonIndex].mappedfreq != ((rds.rdsC & 0xFF) * 10 + 8750) && (rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) { + eon[eonIndex].mappedfreq3 = ((rds.rdsC & 0xFF) * 10 + 8750); + } } } - } - for (int i = 0; i < 20; i++) { - for (int j = 0; j < 20 - i - 1; j++) { - if (eon[j].pi == 0) continue; + for (int i = 0; i < 20; i++) { + for (int j = 0; j < 20 - i - 1; j++) { + if (eon[j].pi == 0) continue; - if (eon[j].pi > eon[j + 1].pi && eon[j + 1].pi != 0) { - std::swap(eon[j].pi, eon[j + 1].pi); - std::swap(eon[j].mappedfreq, eon[j + 1].mappedfreq); - std::swap(eon[j].mappedfreq2, eon[j + 1].mappedfreq2); - std::swap(eon[j].mappedfreq3, eon[j + 1].mappedfreq3); - char temp6[6]; - strncpy(temp6, eon[j].picode, sizeof(temp6) - 1); - temp6[sizeof(temp6) - 1] = '\0'; - strncpy(eon[j].picode, eon[j + 1].picode, sizeof(eon[j].picode) - 1); - eon[j].picode[sizeof(eon[j].picode) - 1] = '\0'; - strncpy(eon[j + 1].picode, temp6, sizeof(eon[j + 1].picode) - 1); - eon[j + 1].picode[sizeof(eon[j + 1].picode) - 1] = '\0'; - std::swap(eon[j].pty, eon[j + 1].pty); - std::swap(eon[j].ps, eon[j + 1].ps); - std::swap(eon[j].ta, eon[j + 1].ta); - std::swap(eon[j].tp, eon[j + 1].tp); - std::swap(eon[j].taset, eon[j + 1].taset); - std::swap(eon_buffer[j], eon_buffer[j + 1]); + if (eon[j].pi > eon[j + 1].pi && eon[j + 1].pi != 0) { + std::swap(eon[j].pi, eon[j + 1].pi); + std::swap(eon[j].mappedfreq, eon[j + 1].mappedfreq); + std::swap(eon[j].mappedfreq2, eon[j + 1].mappedfreq2); + std::swap(eon[j].mappedfreq3, eon[j + 1].mappedfreq3); + char temp6[6]; + strncpy(temp6, eon[j].picode, sizeof(temp6) - 1); + temp6[sizeof(temp6) - 1] = '\0'; + strncpy(eon[j].picode, eon[j + 1].picode, sizeof(eon[j].picode) - 1); + eon[j].picode[sizeof(eon[j].picode) - 1] = '\0'; + strncpy(eon[j + 1].picode, temp6, sizeof(eon[j + 1].picode) - 1); + eon[j + 1].picode[sizeof(eon[j + 1].picode) - 1] = '\0'; + std::swap(eon[j].pty, eon[j + 1].pty); + std::swap(eon[j].ps, eon[j + 1].ps); + std::swap(eon[j].ta, eon[j + 1].ta); + std::swap(eon[j].tp, eon[j + 1].tp); + std::swap(eon[j].taset, eon[j + 1].taset); + std::swap(eon_buffer[j], eon_buffer[j + 1]); + } } } } } - } - break; + break; - case RDS_GROUP_15A: { - if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold)) { - if (pslong_process && rds.stationNameLong.length() > 0) rds.hasLongPS = true; - offset = (rds.rdsB & 0xf) * 4; // Get Long PS character segment + case RDS_GROUP_15A: { + if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold)) { + if (pslong_process && rds.stationNameLong.length() > 0) rds.hasLongPS = true; + offset = (rds.rdsB & 0xf) * 4; // Get Long PS character segment - pslong_buffer2[offset + 0] = pslong_buffer[offset + 0]; // Copy PS long buffer - pslong_buffer2[offset + 1] = pslong_buffer[offset + 1]; - pslong_buffer2[offset + 2] = pslong_buffer[offset + 2]; - pslong_buffer2[offset + 3] = pslong_buffer[offset + 3]; - pslong_buffer2[32] = '\0'; + pslong_buffer2[offset + 0] = pslong_buffer[offset + 0]; // Copy PS long buffer + pslong_buffer2[offset + 1] = pslong_buffer[offset + 1]; + pslong_buffer2[offset + 2] = pslong_buffer[offset + 2]; + pslong_buffer2[offset + 3] = pslong_buffer[offset + 3]; + pslong_buffer2[32] = '\0'; - pslong_buffer[offset + 0] = rds.rdsC >> 8; // First character of segment - pslong_buffer[offset + 1] = rds.rdsC & 0xff; // Second character of segment - pslong_buffer[offset + 2] = rds.rdsD >> 8; // Thirth character of segment - pslong_buffer[offset + 3] = rds.rdsD & 0xff; // Fourth character of segment - pslong_buffer[32] = '\0'; + pslong_buffer[offset + 0] = rds.rdsC >> 8; // First character of segment + pslong_buffer[offset + 1] = rds.rdsC & 0xff; // Second character of segment + pslong_buffer[offset + 2] = rds.rdsD >> 8; // Thirth character of segment + pslong_buffer[offset + 3] = rds.rdsD & 0xff; // Fourth character of segment + pslong_buffer[32] = '\0'; - byte endmarkerLPS = 32; - bool foundendmarker = false; - for (byte i = 0; i < endmarkerLPS; i++) { - if (pslong_buffer[i] == 0x0d) { - foundendmarker = true; - endmarkerLPS = i; - break; + byte endmarkerLPS = 32; + bool foundendmarker = false; + for (byte i = 0; i < endmarkerLPS; i++) { + if (pslong_buffer[i] == 0x0d) { + foundendmarker = true; + endmarkerLPS = i; + break; + } } - } - if ((offset == 0 || foundendmarker) && (pslong_process || !rds.fastps)) { // Last chars are received - if (strcmp(pslong_buffer, pslong_buffer2) == 0) { // When no difference between current and buffer, let's go... - pslong_process = true; - RDScharConverter(pslong_buffer, PSLongtext, sizeof(PSLongtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII - String utf8String = convertToUTF8(PSLongtext); // Convert RDS characterset to ASCII - rds.stationNameLong = extractUTF8Substring(utf8String, 0, endmarkerLPS, true); // Make sure PS Long does not exceed 32 characters + if ((offset == 0 || foundendmarker) && (pslong_process || !rds.fastps)) { // Last chars are received + if (strcmp(pslong_buffer, pslong_buffer2) == 0) { // When no difference between current and buffer, let's go... + pslong_process = true; + RDScharConverter(pslong_buffer, PSLongtext, sizeof(PSLongtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII + String utf8String = convertToUTF8(PSLongtext); // Convert RDS characterset to ASCII + rds.stationNameLong = extractUTF8Substring(utf8String, 0, endmarkerLPS, true); // Make sure PS Long does not exceed 32 characters + rds.stationNameLong = trimTrailingSpaces(rds.stationNameLong); + } + } + + if (!pslong_process && rds.fastps) { // Let's get 2 runs of 32 PS characters fast and without refresh + if (offset == 0) packet0long = true; + if (offset == 4) packet1long = true; + if (offset == 8) packet2long = true; + if (offset == 16) packet3long = true; + RDScharConverter(pslong_buffer, PSLongtext, sizeof(PSLongtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII + String utf8String = convertToUTF8(PSLongtext); // Convert RDS characterset to ASCII + rds.stationNameLong = extractUTF8Substring(utf8String, 0, endmarkerLPS, true); rds.stationNameLong = trimTrailingSpaces(rds.stationNameLong); + if ((packet0long && packet1long && packet2long && packet3long) || foundendmarker) pslong_process = true; // OK, we had one runs, now let's go the idle PS Long writing } } - - if (!pslong_process && rds.fastps) { // Let's get 2 runs of 32 PS characters fast and without refresh - if (offset == 0) packet0long = true; - if (offset == 4) packet1long = true; - if (offset == 8) packet2long = true; - if (offset == 16) packet3long = true; - RDScharConverter(pslong_buffer, PSLongtext, sizeof(PSLongtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII - String utf8String = convertToUTF8(PSLongtext); // Convert RDS characterset to ASCII - rds.stationNameLong = extractUTF8Substring(utf8String, 0, endmarkerLPS, true); - rds.stationNameLong = trimTrailingSpaces(rds.stationNameLong); - if ((packet0long && packet1long && packet2long && packet3long) || foundendmarker) pslong_process = true; // OK, we had one runs, now let's go the idle PS Long writing - } } - } - break; + break; + } + previous_rdsA = rds.rdsA; + previous_rdsB = rds.rdsB; + previous_rdsC = rds.rdsC; + previous_rdsD = rds.rdsD; + } } } diff --git a/src/TEF6686.h b/src/TEF6686.h index 81ff382..fd6f8a7 100644 --- a/src/TEF6686.h +++ b/src/TEF6686.h @@ -582,7 +582,7 @@ typedef struct _rds_ { uint16_t aid[10]; uint32_t dabaffreq; byte aid_counter; - byte fastps; + byte fastps; int8_t offset; unsigned int ECC; unsigned int LIC; @@ -760,11 +760,9 @@ class TEF6686 { bool initab; bool afinit; bool errorfreepi; - bool rdsAerrorThreshold; - bool rdsBerrorThreshold; - bool rdsCerrorThreshold; - bool rdsDerrorThreshold; + bool rdsAerrorThreshold, rdsBerrorThreshold, rdsCerrorThreshold, rdsDerrorThreshold; bool packet0, packet1, packet2, packet3, packet0long, packet1long, packet2long, packet3long; + uint16_t previous_rdsA, previous_rdsB, previous_rdsC, previous_rdsD; bool afmethodBprobe; bool eRTcoding; uint16_t rdsCold; From 62d0fce190bb3847f237860ef8358fdc2d1a14df Mon Sep 17 00:00:00 2001 From: Sjef Verhoeven PE5PVB Date: Mon, 27 May 2024 20:40:17 +0200 Subject: [PATCH 09/13] Fux showing PS in AM when tuning --- TEF6686_ESP32.ino | 2 +- src/rds.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/TEF6686_ESP32.ino b/TEF6686_ESP32.ino index 91a8758..066adb9 100644 --- a/TEF6686_ESP32.ino +++ b/TEF6686_ESP32.ino @@ -1112,7 +1112,7 @@ void GetData() { ShowSignalLevel(); } - if (!menu && (band < BAND_GAP || tunemode == TUNE_MEM)) showPS(); + if (!menu) showPS(); if (band < BAND_GAP && !menu) { if (advancedRDS && !afscreen && !screenmute) ShowAdvancedRDS(); diff --git a/src/rds.cpp b/src/rds.cpp index 8041362..0737799 100644 --- a/src/rds.cpp +++ b/src/rds.cpp @@ -723,7 +723,7 @@ void showPS() { } else { xPos5 = 0; PSSprite.fillSprite(BackgroundColor); - if (!RDSstatus) PSSprite.setTextColor(RDSDropoutColor, RDSDropoutColorSmooth, false); else PSSprite.setTextColor(RDSColor, RDSColorSmooth, false); + if (!RDSstatus || band > BAND_GAP) PSSprite.setTextColor(RDSDropoutColor, RDSDropoutColorSmooth, false); else PSSprite.setTextColor(RDSColor, RDSColorSmooth, false); PSSprite.drawString(radio.rds.stationName, 0, 0); } if (advancedRDS) PSSprite.pushSprite(36, 74); else PSSprite.pushSprite(36, 187); From f22c7e77a8adb77acde255267f387eac5720c1c6 Mon Sep 17 00:00:00 2001 From: Sjef Verhoeven PE5PVB Date: Mon, 27 May 2024 21:30:43 +0200 Subject: [PATCH 10/13] Fixed bug on RDS buffer --- src/TEF6686.cpp | 2321 +++++++++++++++++++++++------------------------ 1 file changed, 1160 insertions(+), 1161 deletions(-) diff --git a/src/TEF6686.cpp b/src/TEF6686.cpp index f10b0f8..e8fd6bf 100644 --- a/src/TEF6686.cpp +++ b/src/TEF6686.cpp @@ -377,843 +377,809 @@ bool TEF6686::getStatusAM(int16_t &level, uint16_t &noise, uint16_t &cochannel, } void TEF6686::readRDS(byte showrdserrors) { - if (millis() - rdstimer > 87) { - rdstimer == millis(); - uint8_t offset; - if (rds.filter) { - devTEF_Radio_Get_RDS_Status(&rds.rdsStat, &rds.rdsA, &rds.rdsB, &rds.rdsC, &rds.rdsD, &rds.rdsErr); - } else { + uint8_t offset; + if (rds.filter && ps_process) { + devTEF_Radio_Get_RDS_Status(&rds.rdsStat, &rds.rdsA, &rds.rdsB, &rds.rdsC, &rds.rdsD, &rds.rdsErr); + } else { + if (millis() >= rdstimer + 87) { + rdstimer += 87; devTEF_Radio_Get_RDS_Data(&rds.rdsStat, &rds.rdsA, &rds.rdsB, &rds.rdsC, &rds.rdsD, &rds.rdsErr); if ((rds.rdsStat & (1 << 14))) { for (int i = 0; i < 22; i++) devTEF_Radio_Get_RDS_Data(&rds.rdsStat, &rds.rdsA, &rds.rdsB, &rds.rdsC, &rds.rdsD, &rds.rdsErr); } } + } - if (bitRead(rds.rdsStat, 9)) { - rds.hasRDS = true; // RDS decoder synchronized and data available - bitStartTime = 0; - } else { - if (bitStartTime == 0) { - bitStartTime = millis(); - } else if (millis() - bitStartTime >= 87) { - rds.hasRDS = false; + if (bitRead(rds.rdsStat, 9)) { + rds.hasRDS = true; // RDS decoder synchronized and data available + bitStartTime = 0; + } else { + if (bitStartTime == 0) { + bitStartTime = millis(); + } else if (millis() - bitStartTime >= 87) { + rds.hasRDS = false; + } + } + + rdsAerrorThreshold = (((rds.rdsErr >> 14) & 0x03) > showrdserrors); + rdsBerrorThreshold = (((rds.rdsErr >> 12) & 0x03) > showrdserrors); + rdsCerrorThreshold = (((rds.rdsErr >> 10) & 0x03) > showrdserrors); + rdsDerrorThreshold = (((rds.rdsErr >> 8) & 0x03) > showrdserrors); + + if (bitRead(rds.rdsStat, 9) && (rds.rdsA != previous_rdsA || rds.rdsB != previous_rdsB || rds.rdsC != previous_rdsC || rds.rdsD != previous_rdsD)) { + rds.rdsAerror = (((rds.rdsErr >> 14) & 0x03) > 1); // We have all data to decode... let's go... + rds.rdsBerror = (((rds.rdsErr >> 12) & 0x03) > 1); + rds.rdsCerror = (((rds.rdsErr >> 10) & 0x03) > 1); + rds.rdsDerror = (((rds.rdsErr >> 8) & 0x03) > 1); + + //PI decoder + if (!rdsAerrorThreshold && afreset) { + rds.correctPI = rds.rdsA; + afreset = false; + } + + if (((!rdsAerrorThreshold && !rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) || (rds.pierrors && !errorfreepi))) { + if (rds.rdsA != piold && rds.rdsA != 0) { + piold = rds.rdsA; + rds.picode[0] = (rds.rdsA >> 12) & 0xF; + rds.picode[1] = (rds.rdsA >> 8) & 0xF; + rds.picode[2] = (rds.rdsA >> 4) & 0xF; + rds.picode[3] = rds.rdsA & 0xF; + for (int i = 0; i < 4; i++) { + if (rds.picode[i] < 10) { + rds.picode[i] += '0'; // Add ASCII offset for decimal digits + } else { + rds.picode[i] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F + } + } + } + + if (!rdsAerrorThreshold && !rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { + rds.picode[4] = ' '; + rds.picode[5] = ' '; + errorfreepi = true; + } + + if (!errorfreepi) { + if (((rds.rdsErr >> 14) & 0x03) > 2) rds.picode[5] = '?'; else rds.picode[5] = ' '; + if (((rds.rdsErr >> 14) & 0x03) > 1) rds.picode[4] = '?'; else rds.picode[4] = ' '; // Not sure, add a ? + } else { + rds.picode[4] = ' '; + rds.picode[5] = ' '; + } + rds.picode[6] = '\0'; + if (strncmp(rds.picode, "0000", 4) == 0) { + if (piold != 0) { + rds.picode[0] = (piold >> 12) & 0xF; + rds.picode[1] = (piold >> 8) & 0xF; + rds.picode[2] = (piold >> 4) & 0xF; + rds.picode[3] = piold & 0xF; + for (int i = 0; i < 4; i++) { + if (rds.picode[i] < 10) { + rds.picode[i] += '0'; // Add ASCII offset for decimal digits + } else { + rds.picode[i] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F + } + } + } else { + if (rds.stationName.length() == 0) { + memset(rds.picode, 0, sizeof(rds.picode)); + } + + memset(rds.picode, 0, sizeof(rds.picode)); + } + } + + // USA Station callsign decoder + if (ps_process && rds.correctPI != 0 && rds.region == 1 && correctPIold != rds.correctPI) { + bool foundMatch = false; + File file; + + if (SPIFFS.begin(true)) { + delay(5); + if (currentfreq2 < 9000) { + file = SPIFFS.open("/USA_87-90.csv"); + } else if (currentfreq2 > 9000 && currentfreq2 < 9200) { + file = SPIFFS.open("/USA_90-92.csv"); + } else if (currentfreq2 > 9200 && currentfreq2 < 9400) { + file = SPIFFS.open("/USA_92-94.csv"); + } else if (currentfreq2 > 9400 && currentfreq2 < 9600) { + file = SPIFFS.open("/USA_94-96.csv"); + } else if (currentfreq2 > 9600 && currentfreq2 < 9800) { + file = SPIFFS.open("/USA_96-98.csv"); + } else if (currentfreq2 > 9800 && currentfreq2 < 10000) { + file = SPIFFS.open("/USA_98-100.csv"); + } else if (currentfreq2 > 10000 && currentfreq2 < 10200) { + file = SPIFFS.open("/USA_100-102.csv"); + } else if (currentfreq2 > 10200 && currentfreq2 < 10400) { + file = SPIFFS.open("/USA_102-104.csv"); + } else if (currentfreq2 > 10400 && currentfreq2 < 10600) { + file = SPIFFS.open("/USA_104-106.csv"); + } else if (currentfreq2 > 10600) { + file = SPIFFS.open("/USA_106-108.csv"); + } + + delay(5); + if (file) { + int i = 0; + while (file.available() && !isprint(file.peek())) { + file.read(); + i++; + } + + char buffer[25]; + while (file.available()) { + int bytesRead = file.readBytesUntil('\n', buffer, sizeof(buffer) - 1); + buffer[bytesRead] = '\0'; + + char *token = strtok(buffer, ";"); + + int firstColumnValue = 0; + uint16_t frequencyValue = 0; + char stationID[8]; + char stationState[8]; + + if (token) { + firstColumnValue = atoi(token); + token = strtok(NULL, ";"); + frequencyValue = atoi(token); + token = strtok(NULL, ";"); + strncpy(stationID, token, sizeof(stationID) - 1); + stationID[sizeof(stationID) - 1] = '\0'; + token = strtok(NULL, ";"); + strncpy(stationState, token, sizeof(stationState) - 1); + stationState[sizeof(stationState) - 1] = '\0'; + } + + if (frequencyValue == currentfreq2 && static_cast(firstColumnValue) == rds.correctPI) { + strncpy(rds.stationID, stationID, 7); + strncpy(rds.stationState, stationState, 2); + foundMatch = true; + break; + } + } + } + } + + if (!foundMatch) { + uint16_t stationID = rds.rdsA; + if (stationID > 4096) { + if (stationID > 21671 && (stationID & 0xF00U) >> 8 == 0) stationID = ((uint16_t)uint8_t(0xA0 + ((stationID & 0xF000U) >> 12)) << 8) + lowByte(stationID); // C0DE -> ACDE + if (stationID > 21671 && lowByte(stationID) == 0) stationID = 0xAF00 + uint8_t(highByte(stationID)); // CD00 -> AFCD + if (stationID < 39247) { + if (stationID > 21671) { + rds.stationID[0] = 'W'; + stationID -= 21672; + } else { + rds.stationID[0] = 'K'; + stationID -= 4096; + } + rds.stationID[1] = char(stationID / 676 + 65); + rds.stationID[2] = char((stationID - 676 * int(stationID / 676)) / 26 + 65); + rds.stationID[3] = char(((stationID - 676 * int(stationID / 676)) % 26) + 65); + } else { + stationID -= 4835; + rds.stationID[0] = 'K'; + rds.stationID[1] = char(stationID / 676 + 65); + rds.stationID[2] = char((stationID - 676 * int(stationID / 676)) / 26 + 65); + rds.stationID[3] = char(((stationID - 676 * int(stationID / 676)) % 26) + 65); + } + } + + bool faultyID = false; + + for (byte i = 0; i < 4; i++) { + if (!(rds.stationID[i] >= 'A' && rds.stationID[i] <= 'Z')) { + faultyID = true; + break; + } + } + + if (faultyID) { + strcpy(rds.stationID, "Unknown"); + } else { + rds.stationID[7] = '?'; + } + rds.stationID[8] = '\0'; + } + correctPIold = rds.correctPI; + rds.stationIDtext = rds.stationID; + rds.stationStatetext = rds.stationState; } } - rdsAerrorThreshold = (((rds.rdsErr >> 14) & 0x03) > showrdserrors); - rdsBerrorThreshold = (((rds.rdsErr >> 12) & 0x03) > showrdserrors); - rdsCerrorThreshold = (((rds.rdsErr >> 10) & 0x03) > showrdserrors); - rdsDerrorThreshold = (((rds.rdsErr >> 8) & 0x03) > showrdserrors); + if (!rds.rdsBerror || showrdserrors == 3) rdsblock = rds.rdsB >> 11; else return; + switch (rdsblock) { + case RDS_GROUP_0A: + case RDS_GROUP_0B: + { + //PS decoder + if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsDerrorThreshold)) { + offset = rds.rdsB & 0x03; // Let's get the character offset for PS - if (bitRead(rds.rdsStat, 9) && (rds.rdsA != previous_rdsA || rds.rdsB != previous_rdsB || rds.rdsC != previous_rdsC || rds.rdsD != previous_rdsD)) { - rds.rdsAerror = (((rds.rdsErr >> 14) & 0x03) > 1); // We have all data to decode... let's go... - rds.rdsBerror = (((rds.rdsErr >> 12) & 0x03) > 1); - rds.rdsCerror = (((rds.rdsErr >> 10) & 0x03) > 1); - rds.rdsDerror = (((rds.rdsErr >> 8) & 0x03) > 1); + ps_buffer2[(offset * 2) + 0] = ps_buffer[(offset * 2) + 0]; // Make a copy of the PS buffer + ps_buffer2[(offset * 2) + 1] = ps_buffer[(offset * 2) + 1]; + ps_buffer2[8] = '\0'; // Endmarker - //PI decoder - if (!rdsAerrorThreshold && afreset) { - rds.correctPI = rds.rdsA; - afreset = false; - } + ps_buffer[(offset * 2) + 0] = rds.rdsD >> 8; // First character of segment + ps_buffer[(offset * 2) + 1] = rds.rdsD & 0xFF; // Second character of segment + ps_buffer[8] = '\0'; // Endmarker - if (((!rdsAerrorThreshold && !rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) || (rds.pierrors && !errorfreepi))) { - if (rds.rdsA != piold && rds.rdsA != 0) { - piold = rds.rdsA; - rds.picode[0] = (rds.rdsA >> 12) & 0xF; - rds.picode[1] = (rds.rdsA >> 8) & 0xF; - rds.picode[2] = (rds.rdsA >> 4) & 0xF; - rds.picode[3] = rds.rdsA & 0xF; - for (int i = 0; i < 4; i++) { - if (rds.picode[i] < 10) { - rds.picode[i] += '0'; // Add ASCII offset for decimal digits - } else { - rds.picode[i] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F - } - } - } - - if (!rdsAerrorThreshold && !rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { - rds.picode[4] = ' '; - rds.picode[5] = ' '; - errorfreepi = true; - } - - if (!errorfreepi) { - if (((rds.rdsErr >> 14) & 0x03) > 2) rds.picode[5] = '?'; else rds.picode[5] = ' '; - if (((rds.rdsErr >> 14) & 0x03) > 1) rds.picode[4] = '?'; else rds.picode[4] = ' '; // Not sure, add a ? - } else { - rds.picode[4] = ' '; - rds.picode[5] = ' '; - } - rds.picode[6] = '\0'; - if (strncmp(rds.picode, "0000", 4) == 0) { - if (piold != 0) { - rds.picode[0] = (piold >> 12) & 0xF; - rds.picode[1] = (piold >> 8) & 0xF; - rds.picode[2] = (piold >> 4) & 0xF; - rds.picode[3] = piold & 0xF; - for (int i = 0; i < 4; i++) { - if (rds.picode[i] < 10) { - rds.picode[i] += '0'; // Add ASCII offset for decimal digits - } else { - rds.picode[i] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F + if (ps_process || rds.fastps == 0) { + if (offset == 0) { + packet0 = true; + packet1 = false; + packet2 = false; + packet3 = false; } - } - } else { - if (rds.stationName.length() == 0) { - memset(rds.picode, 0, sizeof(rds.picode)); + if (offset == 1) packet1 = true; + if (offset == 2) packet2 = true; + if (offset == 3) packet3 = true; } - memset(rds.picode, 0, sizeof(rds.picode)); - } - } - - // USA Station callsign decoder - if (ps_process && rds.correctPI != 0 && rds.region == 1 && correctPIold != rds.correctPI) { - bool foundMatch = false; - File file; - - if (SPIFFS.begin(true)) { - delay(5); - if (currentfreq2 < 9000) { - file = SPIFFS.open("/USA_87-90.csv"); - } else if (currentfreq2 > 9000 && currentfreq2 < 9200) { - file = SPIFFS.open("/USA_90-92.csv"); - } else if (currentfreq2 > 9200 && currentfreq2 < 9400) { - file = SPIFFS.open("/USA_92-94.csv"); - } else if (currentfreq2 > 9400 && currentfreq2 < 9600) { - file = SPIFFS.open("/USA_94-96.csv"); - } else if (currentfreq2 > 9600 && currentfreq2 < 9800) { - file = SPIFFS.open("/USA_96-98.csv"); - } else if (currentfreq2 > 9800 && currentfreq2 < 10000) { - file = SPIFFS.open("/USA_98-100.csv"); - } else if (currentfreq2 > 10000 && currentfreq2 < 10200) { - file = SPIFFS.open("/USA_100-102.csv"); - } else if (currentfreq2 > 10200 && currentfreq2 < 10400) { - file = SPIFFS.open("/USA_102-104.csv"); - } else if (currentfreq2 > 10400 && currentfreq2 < 10600) { - file = SPIFFS.open("/USA_104-106.csv"); - } else if (currentfreq2 > 10600) { - file = SPIFFS.open("/USA_106-108.csv"); - } - - delay(5); - if (file) { - int i = 0; - while (file.available() && !isprint(file.peek())) { - file.read(); - i++; - } - - char buffer[25]; - while (file.available()) { - int bytesRead = file.readBytesUntil('\n', buffer, sizeof(buffer) - 1); - buffer[bytesRead] = '\0'; - - char *token = strtok(buffer, ";"); - - int firstColumnValue = 0; - uint16_t frequencyValue = 0; - char stationID[8]; - char stationState[8]; - - if (token) { - firstColumnValue = atoi(token); - token = strtok(NULL, ";"); - frequencyValue = atoi(token); - token = strtok(NULL, ";"); - strncpy(stationID, token, sizeof(stationID) - 1); - stationID[sizeof(stationID) - 1] = '\0'; - token = strtok(NULL, ";"); - strncpy(stationState, token, sizeof(stationState) - 1); - stationState[sizeof(stationState) - 1] = '\0'; - } - - if (frequencyValue == currentfreq2 && static_cast(firstColumnValue) == rds.correctPI) { - strncpy(rds.stationID, stationID, 7); - strncpy(rds.stationState, stationState, 2); - foundMatch = true; - break; + if (packet0 && packet1 && packet2 && packet3 && (ps_process || (rds.fastps == 0 && rds.fastps != 2))) { // Last chars are received + if (strcmp(ps_buffer, ps_buffer2) == 0) { // When no difference between current and buffer, let's go... + ps_process = true; + RDScharConverter(ps_buffer2, PStext, sizeof(PStext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII + String utf8String = convertToUTF8(PStext); // Convert RDS characterset to ASCII + rds.stationName = extractUTF8Substring(utf8String, 0, 8, true); // Make sure PS does not exceed 8 characters + for (byte x = 0; x < 8; x++) { + ps_buffer[x] = '\0'; + ps_buffer2[x] = '\0'; } } } + + if ((!ps_process && rds.fastps > 0 && rds.fastps != 2) || rds.fastps == 2) { // Let's get 2 runs of 8 PS characters fast and without refresh + if (offset == 0) packet0 = true; + if (offset == 1) packet1 = true; + if (offset == 2) packet2 = true; + if (offset == 3) packet3 = true; + RDScharConverter(ps_buffer, PStext, sizeof(PStext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII + String utf8String = convertToUTF8(PStext); // Convert RDS characterset to ASCII + rds.stationName = extractUTF8Substring(utf8String, 0, 8, true); + if (packet0 && packet1 && packet2 && packet3) ps_process = true; // OK, we had one runs, now let's go the idle PS writing + } + + if (offset == 0) rds.hasDynamicPTY = bitRead(rds.rdsB, 2) & 0x1F; // Dynamic PTY flag + if (offset == 1) rds.hasCompressed = bitRead(rds.rdsB, 2) & 0x1F; // Compressed flag + if (offset == 2) rds.hasArtificialhead = bitRead(rds.rdsB, 2) & 0x1F; // Artificial head flag + if (offset == 3) rds.hasStereo = bitRead(rds.rdsB, 2) & 0x1F; // Stereo flag } - if (!foundMatch) { - uint16_t stationID = rds.rdsA; - if (stationID > 4096) { - if (stationID > 21671 && (stationID & 0xF00U) >> 8 == 0) stationID = ((uint16_t)uint8_t(0xA0 + ((stationID & 0xF000U) >> 12)) << 8) + lowByte(stationID); // C0DE -> ACDE - if (stationID > 21671 && lowByte(stationID) == 0) stationID = 0xAF00 + uint8_t(highByte(stationID)); // CD00 -> AFCD - if (stationID < 39247) { - if (stationID > 21671) { - rds.stationID[0] = 'W'; - stationID -= 21672; - } else { - rds.stationID[0] = 'K'; - stationID -= 4096; + if (!rdsBerrorThreshold) { + rds.stationTypeCode = (rds.rdsB >> 5) & 0x1F; // Get 5 PTY bits from Block B + if (rds.region == 0) strcpy(rds.stationType, PTY_EU[rds.stationTypeCode]); + if (rds.region == 1) strcpy(rds.stationType, PTY_USA[rds.stationTypeCode]); + + rds.hasTA = (bitRead(rds.rdsB, 4)); // Read TA flag + + if ((bitRead(rds.rdsB, 3)) == 1) rds.MS = 1; else rds.MS = 2; // Read MS flag + } + + rds.hasTP = (bitRead(rds.rdsB, 10)); // Read TP flag + + if (!rdsCerrorThreshold) { + //AF decoder + if (rdsblock == 0 && rds.rdsC != rdsCold) { // Only when in GROUP 0A + + if ((rds.rdsC >> 8) > 224 && (rds.rdsC >> 8) < 250) { + afinit = true; // AF detected + rds.hasAF = true; + } + + if (afinit) { + if ((rds.rdsC >> 8) > 224 && (rds.rdsC >> 8) < 250 && ((rds.rdsC & 0xFF) * 10 + 8750) == currentfreq && rds.hasAF) { + if (afmethodBtrigger) afmethodB = true; // Check for AF method B + afmethodBprobe = true; + af_updatecounter++; + af_counterb = (rds.rdsC >> 8) - 224; + af_number = (rds.rdsC >> 8) - 224; + af_counterbcheck = 1; + doublecounter = 0; + doubletestfreq = (rds.rdsC & 0xFF) * 10 + 8750; + } else if ((rds.rdsC >> 8) > 224 && (rds.rdsC >> 8) < 250 && ((rds.rdsC & 0xFF) * 10 + 8750) != currentfreq && rds.hasAF) { + afmethodBprobe = false; + afmethodBtrigger = true; + af_counterb = 0; + af_number = (rds.rdsC >> 8) - 224; + af_counterbcheck = 0; + doublecounter = 0; + doubletestfreq = (rds.rdsC & 0xFF) * 10 + 8750; } - rds.stationID[1] = char(stationID / 676 + 65); - rds.stationID[2] = char((stationID - 676 * int(stationID / 676)) / 26 + 65); - rds.stationID[3] = char(((stationID - 676 * int(stationID / 676)) % 26) + 65); - } else { - stationID -= 4835; - rds.stationID[0] = 'K'; - rds.stationID[1] = char(stationID / 676 + 65); - rds.stationID[2] = char((stationID - 676 * int(stationID / 676)) / 26 + 65); - rds.stationID[3] = char(((stationID - 676 * int(stationID / 676)) % 26) + 65); + + if (((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) && ((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205)) { + if (afmethodBprobe) af_counterbcheck += 2; + } + + if ((rds.rdsB >> 11) == 0 && af_counter < 50) { + uint16_t buffer0; + uint16_t buffer1; + + if ((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) buffer0 = (rds.rdsC >> 8) * 10 + 8750; else buffer0 = 0; + if ((rds.rdsC & 0xFF) > 0 && (rds.rdsC & 0xFF) < 205) buffer1 = (rds.rdsC & 0xFF) * 10 + 8750; else buffer1 = 0; + + if (((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) && (buffer0 == doubletestfreq || buffer1 == doubletestfreq)) doublecounter++; + if (doublecounter > (af_number / 2)) afmethodB = true; // If signed frequency also appears more than once in the AF list, AF Method B detected + + if (afmethodBprobe && af_counterbcheck > af_counterb) afmethodBprobe = false; // If more than counter received disable probe flag + + if (afmethodBprobe) { // Check for Reg. flags + if (buffer1 == currentfreq && buffer0 > buffer1) { + for (int x = 0; x < af_counter; x++) { + if (af[x].frequency == buffer0 && !af[x].regional) { + af[x].regional = true; + af_updatecounter++; + break; + } + } + } else if (buffer1 == currentfreq && buffer0 < buffer1) { + for (int x = 0; x < af_counter; x++) { + if (af[x].frequency == buffer0 && !af[x].same) { + af[x].same = true; + af_updatecounter++; + break; + } + } + } + + if (buffer0 == currentfreq && buffer0 > buffer1) { + for (int x = 0; x < af_counter; x++) { + if (af[x].frequency == buffer1 && !af[x].regional) { + af[x].regional = true; + af_updatecounter++; + break; + } + } + } else if (buffer0 == currentfreq && buffer0 < buffer1) { + for (int x = 0; x < af_counter; x++) { + if (af[x].frequency == buffer1 && !af[x].same) { + af[x].same = true; + af_updatecounter++; + break; + } + } + } + } + + if (buffer0 != currentfreq && buffer1 != currentfreq && afmethodB && afmethodBprobe) { + afmethodBprobe = false; // Remove faulty Reg. flags + for (int x = 0; x < af_counter; x++) { + if (af[x].frequency == buffer0 || af[x].frequency == buffer1) { + if (af[x].same) { + af[x].same = false; + af_updatecounter++; + } + if (af[x].regional) { + af[x].regional = false; + af_updatecounter++; + } + } + break; + } + } + + bool isValuePresent = false; + for (int i = 0; i < 50; i++) { // Check if already in list + if ((rds.sortaf && (buffer0 == currentfreq)) || buffer0 == 0 || af[i].frequency == buffer0) { + isValuePresent = true; + break; + } + } + + if (!isValuePresent) { // Add frequency to list + af[af_counter].frequency = buffer0; + if (af_counter < 50) af_counter++; + af_updatecounter++; + } + + isValuePresent = false; + for (int i = 0; i < 50; i++) { // Check if already in list + if ((rds.sortaf && (buffer1 == currentfreq)) || buffer1 == 0 || af[i].frequency == buffer1) { + isValuePresent = true; + break; + } + } + + if (!isValuePresent) { + af[af_counter].frequency = buffer1; // Add frequency to list + if (af_counter < 50) af_counter++; + af_updatecounter++; + } + + if (rds.sortaf) { // Sort AF list (low to high) + for (int i = 0; i < 50; i++) { + for (int j = 0; j < 50 - i; j++) { + if (af[j].frequency == 0) continue; + + if (af[j].frequency > af[j + 1].frequency && af[j + 1].frequency != 0) { + uint16_t temp = af[j].frequency; + bool temp3 = af[j].afvalid; + bool temp4 = af[j].checked; + bool temp5 = af[j].regional; + bool temp6 = af[j].same; + af[j].frequency = af[j + 1].frequency; + af[j].afvalid = af[j + 1].afvalid; + af[j].checked = af[j + 1].checked; + af[j].regional = af[j + 1].regional; + af[j].same = af[j + 1].same; + af[j + 1].frequency = temp; + af[j + 1].afvalid = temp3; + af[j + 1].checked = temp4; + af[j + 1].regional = temp5; + af[j + 1].same = temp6; + } + } + } + } + } + } + rdsCold = rds.rdsC; + } + } + } break; + + case RDS_GROUP_1A: + case RDS_GROUP_1B: { + if (!rdsCerrorThreshold) { + if (((rds.rdsC >> 12) & 0x07) == 0 && rdsblock == RDS_GROUP_1A) { // ECC code readout + rds.ECC = rds.rdsC & 0xff; + rds.hasECC = true; + + switch (rds.picode[0]) { + case '1': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[129]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[136]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[152]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[104]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[73]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[83]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[0]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[1]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[2]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[59]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[3]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[179]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[197]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[124]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[200]; + break; + } + + case '2': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[130]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[141]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[155]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[106]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[126]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[95]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[4]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[5]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[6]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[7]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[8]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[180]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[187]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[77]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[218]; + break; + } + + case '3': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[224]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[156]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[132]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[112]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[119]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[72]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[92]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[9]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[10]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[11]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[12]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[16]; + if (rds.ECC == 229) rds.ECCtext = ECCtext[63]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[181]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[227]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[189]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[219]; + break; + } + + case '4': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[148]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[157]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[127]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[69]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[91]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[103]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[13]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[14]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[15]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[182]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[210]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[213]; + break; + } + + case '5': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[133]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[159]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[68]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[79]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[88]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[78]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[17]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[18]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[19]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[65]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[183]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[191]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[193]; + break; + } + + case '6': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[134]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[163]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[171]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[96]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[105]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[123]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[125]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[20]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[21]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[22]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[24]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[184]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[76]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[201]; + break; + } + + case '7': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[139]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[161]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[164]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[113]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[81]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[84]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[25]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[26]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[27]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[62]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[185]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[207]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[221]; + break; + } + + case '8': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[142]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[170]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[116]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[117]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[74]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[82]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[28]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[29]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[30]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[31]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[32]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[186]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[195]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[212]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[190]; + break; + } + + case '9': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[143]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[162]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[174]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[120]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[102]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[107]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[33]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[34]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[35]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[36]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[37]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[80]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[209]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[196]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[211]; + if (rds.ECC == 244) rds.ECCtext = ECCtext[190]; + break; + } + + case 'A': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[131]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[144]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[166]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[85]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[97]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[121]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[70]; + if (rds.ECC == 212) rds.ECCtext = ECCtext[86]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[38]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[39]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[40]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[41]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[60]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[178]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[215]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[214]; + break; + } + + case 'B': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[137]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[145]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[167]; + if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[101]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[109]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[100]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[42]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[43]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[44]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[61]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[66]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[206]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[188]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[203]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[93]; + if (rds.ECC == 244) rds.ECCtext = ECCtext[204]; + break; + } + + case 'C': { + if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[223]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[140]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[146]; + if (rds.ECC == 165) rds.ECCtext = ECCtext[169]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[110]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[108]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[111]; + if (rds.ECC == 211) rds.ECCtext = ECCtext[87]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[45]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[46]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[47]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[48]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[118]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[190]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[216]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[194]; + break; + } + + case 'D': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[225]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[150]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[154]; + if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[90]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[89]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[0]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[49]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[23]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[122]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[198]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[217]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[128]; + break; + } + + case 'E': { + if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; + if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[151]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[172]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[175]; + if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[99]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[115]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[94]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[50]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[51]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[52]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[53]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[64]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[98]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[199]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[208]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[205]; + break; + } + + case 'F': { + if (rds.ECC == 161) rds.ECCtext = ECCtext[149]; + if (rds.ECC == 162) rds.ECCtext = ECCtext[222]; + if (rds.ECC == 163) rds.ECCtext = ECCtext[153]; + if (rds.ECC == 164) rds.ECCtext = ECCtext[176]; + if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; + if (rds.ECC == 166) rds.ECCtext = ECCtext[168]; + if (rds.ECC == 208) rds.ECCtext = ECCtext[67]; + if (rds.ECC == 209) rds.ECCtext = ECCtext[75]; + if (rds.ECC == 210) rds.ECCtext = ECCtext[114]; + if (rds.ECC == 224) rds.ECCtext = ECCtext[54]; + if (rds.ECC == 225) rds.ECCtext = ECCtext[55]; + if (rds.ECC == 226) rds.ECCtext = ECCtext[56]; + if (rds.ECC == 227) rds.ECCtext = ECCtext[57]; + if (rds.ECC == 228) rds.ECCtext = ECCtext[58]; + if (rds.ECC == 240) rds.ECCtext = ECCtext[202]; + if (rds.ECC == 241) rds.ECCtext = ECCtext[192]; + if (rds.ECC == 242) rds.ECCtext = ECCtext[220]; + if (rds.ECC == 243) rds.ECCtext = ECCtext[71]; + break; + } + default: { + rds.ECCtext = ""; + break; + } } } - bool faultyID = false; + if (((rds.rdsC >> 12) & 0x07) == 3 && rdsblock == RDS_GROUP_1A) { // LIC code readout + rds.LIC = rds.rdsC & 0xff; + rds.hasLIC = true; + if (rds.LIC > 0 && rds.LIC < 128) rds.LICtext = LICtext[rds.LIC]; else rds.LICtext = ""; + } + } - for (byte i = 0; i < 4; i++) { - if (!(rds.stationID[i] >= 'A' && rds.stationID[i] <= 'Z')) { - faultyID = true; + if (rds.rdsC >> 12 == 1 && rdsblock == RDS_GROUP_1A) rds.hasTMC = true; // TMC flag + + if (!rdsDerrorThreshold) { + if (rds.rdsD != 0) { // PIN decoder + if ((rds.rdsD & 0x3f) < 61 && ((rds.rdsD >> 6) & 0x1f) < 24) { + rds.hasPIN = true; + rds.pinMin = rds.rdsD & 0x3f; + rds.pinHour = rds.rdsD >> 6 & 0x1f; + rds.pinDay = rds.rdsD >> 11 & 0x1f; + } + } + } + } break; + + case RDS_GROUP_2A: { + if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold)) { + // RT decoder (64 characters) + rds.hasRT = true; + rds.rtAB = (bitRead(rds.rdsB, 4)); // Get AB flag + + if (initab) { + rtABold = rds.rtAB; + initab = false; + } + + byte endmarkerRT64 = 64; + for (byte i = 0; i < endmarkerRT64; i++) { + if (rt_buffer[i] == 0x0d) { + endmarkerRT64 = i; break; } } - if (faultyID) { - strcpy(rds.stationID, "Unknown"); - } else { - rds.stationID[7] = '?'; - } - rds.stationID[8] = '\0'; - } - correctPIold = rds.correctPI; - rds.stationIDtext = rds.stationID; - rds.stationStatetext = rds.stationState; - } - } - - if (!rds.rdsBerror || showrdserrors == 3) rdsblock = rds.rdsB >> 11; else return; - switch (rdsblock) { - case RDS_GROUP_0A: - case RDS_GROUP_0B: - { - //PS decoder - if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsDerrorThreshold)) { - offset = rds.rdsB & 0x03; // Let's get the character offset for PS - - ps_buffer2[(offset * 2) + 0] = ps_buffer[(offset * 2) + 0]; // Make a copy of the PS buffer - ps_buffer2[(offset * 2) + 1] = ps_buffer[(offset * 2) + 1]; - ps_buffer2[8] = '\0'; // Endmarker - - ps_buffer[(offset * 2) + 0] = rds.rdsD >> 8; // First character of segment - ps_buffer[(offset * 2) + 1] = rds.rdsD & 0xFF; // Second character of segment - ps_buffer[8] = '\0'; // Endmarker - - if (ps_process || rds.fastps == 0) { - if (offset == 0) { - packet0 = true; - packet1 = false; - packet2 = false; - packet3 = false; - } - if (offset == 1) packet1 = true; - if (offset == 2) packet2 = true; - if (offset == 3) packet3 = true; - } - - if (packet0 && packet1 && packet2 && packet3 && (ps_process || rds.fastps == 0)) { // Last chars are received - if (strcmp(ps_buffer, ps_buffer2) == 0) { // When no difference between current and buffer, let's go... - ps_process = true; - RDScharConverter(ps_buffer2, PStext, sizeof(PStext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII - String utf8String = convertToUTF8(PStext); // Convert RDS characterset to ASCII - rds.stationName = extractUTF8Substring(utf8String, 0, 8, true); // Make sure PS does not exceed 8 characters - for (byte x = 0; x < 8; x++) { - ps_buffer[x] = '\0'; - ps_buffer2[x] = '\0'; - } - } - } - - if (!ps_process && rds.fastps > 0) { // Let's get 2 runs of 8 PS characters fast and without refresh - if (offset == 0) packet0 = true; - if (offset == 1) packet1 = true; - if (offset == 2) packet2 = true; - if (offset == 3) packet3 = true; - RDScharConverter(ps_buffer, PStext, sizeof(PStext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII - String utf8String = convertToUTF8(PStext); // Convert RDS characterset to ASCII - rds.stationName = extractUTF8Substring(utf8String, 0, 8, true); - if (packet0 && packet1 && packet2 && packet3 && rds.fastps < 2) ps_process = true;// OK, we had one runs, now let's go the idle PS writing - } - - if (offset == 0) rds.hasDynamicPTY = bitRead(rds.rdsB, 2) & 0x1F; // Dynamic PTY flag - if (offset == 1) rds.hasCompressed = bitRead(rds.rdsB, 2) & 0x1F; // Compressed flag - if (offset == 2) rds.hasArtificialhead = bitRead(rds.rdsB, 2) & 0x1F; // Artificial head flag - if (offset == 3) rds.hasStereo = bitRead(rds.rdsB, 2) & 0x1F; // Stereo flag - } - - if (!rdsBerrorThreshold) { - rds.stationTypeCode = (rds.rdsB >> 5) & 0x1F; // Get 5 PTY bits from Block B - if (rds.region == 0) strcpy(rds.stationType, PTY_EU[rds.stationTypeCode]); - if (rds.region == 1) strcpy(rds.stationType, PTY_USA[rds.stationTypeCode]); - - rds.hasTA = (bitRead(rds.rdsB, 4)); // Read TA flag - - if ((bitRead(rds.rdsB, 3)) == 1) rds.MS = 1; else rds.MS = 2; // Read MS flag - } - - rds.hasTP = (bitRead(rds.rdsB, 10)); // Read TP flag - - if (!rdsCerrorThreshold) { - //AF decoder - if (rdsblock == 0 && rds.rdsC != rdsCold) { // Only when in GROUP 0A - - if ((rds.rdsC >> 8) > 224 && (rds.rdsC >> 8) < 250) { - afinit = true; // AF detected - rds.hasAF = true; - } - - if (afinit) { - if ((rds.rdsC >> 8) > 224 && (rds.rdsC >> 8) < 250 && ((rds.rdsC & 0xFF) * 10 + 8750) == currentfreq && rds.hasAF) { - if (afmethodBtrigger) afmethodB = true; // Check for AF method B - afmethodBprobe = true; - af_updatecounter++; - af_counterb = (rds.rdsC >> 8) - 224; - af_number = (rds.rdsC >> 8) - 224; - af_counterbcheck = 1; - doublecounter = 0; - doubletestfreq = (rds.rdsC & 0xFF) * 10 + 8750; - } else if ((rds.rdsC >> 8) > 224 && (rds.rdsC >> 8) < 250 && ((rds.rdsC & 0xFF) * 10 + 8750) != currentfreq && rds.hasAF) { - afmethodBprobe = false; - afmethodBtrigger = true; - af_counterb = 0; - af_number = (rds.rdsC >> 8) - 224; - af_counterbcheck = 0; - doublecounter = 0; - doubletestfreq = (rds.rdsC & 0xFF) * 10 + 8750; - } - - if (((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) && ((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205)) { - if (afmethodBprobe) af_counterbcheck += 2; - } - - if ((rds.rdsB >> 11) == 0 && af_counter < 50) { - uint16_t buffer0; - uint16_t buffer1; - - if ((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) buffer0 = (rds.rdsC >> 8) * 10 + 8750; else buffer0 = 0; - if ((rds.rdsC & 0xFF) > 0 && (rds.rdsC & 0xFF) < 205) buffer1 = (rds.rdsC & 0xFF) * 10 + 8750; else buffer1 = 0; - - if (((rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) && (buffer0 == doubletestfreq || buffer1 == doubletestfreq)) doublecounter++; - if (doublecounter > (af_number / 2)) afmethodB = true; // If signed frequency also appears more than once in the AF list, AF Method B detected - - if (afmethodBprobe && af_counterbcheck > af_counterb) afmethodBprobe = false; // If more than counter received disable probe flag - - if (afmethodBprobe) { // Check for Reg. flags - if (buffer1 == currentfreq && buffer0 > buffer1) { - for (int x = 0; x < af_counter; x++) { - if (af[x].frequency == buffer0 && !af[x].regional) { - af[x].regional = true; - af_updatecounter++; - break; - } - } - } else if (buffer1 == currentfreq && buffer0 < buffer1) { - for (int x = 0; x < af_counter; x++) { - if (af[x].frequency == buffer0 && !af[x].same) { - af[x].same = true; - af_updatecounter++; - break; - } - } - } - - if (buffer0 == currentfreq && buffer0 > buffer1) { - for (int x = 0; x < af_counter; x++) { - if (af[x].frequency == buffer1 && !af[x].regional) { - af[x].regional = true; - af_updatecounter++; - break; - } - } - } else if (buffer0 == currentfreq && buffer0 < buffer1) { - for (int x = 0; x < af_counter; x++) { - if (af[x].frequency == buffer1 && !af[x].same) { - af[x].same = true; - af_updatecounter++; - break; - } - } - } - } - - if (buffer0 != currentfreq && buffer1 != currentfreq && afmethodB && afmethodBprobe) { - afmethodBprobe = false; // Remove faulty Reg. flags - for (int x = 0; x < af_counter; x++) { - if (af[x].frequency == buffer0 || af[x].frequency == buffer1) { - if (af[x].same) { - af[x].same = false; - af_updatecounter++; - } - if (af[x].regional) { - af[x].regional = false; - af_updatecounter++; - } - } - break; - } - } - - bool isValuePresent = false; - for (int i = 0; i < 50; i++) { // Check if already in list - if ((rds.sortaf && (buffer0 == currentfreq)) || buffer0 == 0 || af[i].frequency == buffer0) { - isValuePresent = true; - break; - } - } - - if (!isValuePresent) { // Add frequency to list - af[af_counter].frequency = buffer0; - if (af_counter < 50) af_counter++; - af_updatecounter++; - } - - isValuePresent = false; - for (int i = 0; i < 50; i++) { // Check if already in list - if ((rds.sortaf && (buffer1 == currentfreq)) || buffer1 == 0 || af[i].frequency == buffer1) { - isValuePresent = true; - break; - } - } - - if (!isValuePresent) { - af[af_counter].frequency = buffer1; // Add frequency to list - if (af_counter < 50) af_counter++; - af_updatecounter++; - } - - if (rds.sortaf) { // Sort AF list (low to high) - for (int i = 0; i < 50; i++) { - for (int j = 0; j < 50 - i; j++) { - if (af[j].frequency == 0) continue; - - if (af[j].frequency > af[j + 1].frequency && af[j + 1].frequency != 0) { - uint16_t temp = af[j].frequency; - bool temp3 = af[j].afvalid; - bool temp4 = af[j].checked; - bool temp5 = af[j].regional; - bool temp6 = af[j].same; - af[j].frequency = af[j + 1].frequency; - af[j].afvalid = af[j + 1].afvalid; - af[j].checked = af[j + 1].checked; - af[j].regional = af[j + 1].regional; - af[j].same = af[j + 1].same; - af[j + 1].frequency = temp; - af[j + 1].afvalid = temp3; - af[j + 1].checked = temp4; - af[j + 1].regional = temp5; - af[j + 1].same = temp6; - } - } - } - } - } - } - rdsCold = rds.rdsC; - } - } - } break; - - case RDS_GROUP_1A: - case RDS_GROUP_1B: { - if (!rdsCerrorThreshold) { - if (((rds.rdsC >> 12) & 0x07) == 0 && rdsblock == RDS_GROUP_1A) { // ECC code readout - rds.ECC = rds.rdsC & 0xff; - rds.hasECC = true; - - switch (rds.picode[0]) { - case '1': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[129]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[136]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[152]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[104]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[73]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[83]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[0]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[1]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[2]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[59]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[3]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[179]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[197]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[124]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[200]; - break; - } - - case '2': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[130]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[141]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[155]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[106]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[126]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[95]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[4]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[5]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[6]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[7]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[8]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[180]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[187]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[77]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[218]; - break; - } - - case '3': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[224]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[156]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[132]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[112]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[119]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[72]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[92]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[9]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[10]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[11]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[12]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[16]; - if (rds.ECC == 229) rds.ECCtext = ECCtext[63]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[181]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[227]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[189]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[219]; - break; - } - - case '4': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[148]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[157]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[127]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[69]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[91]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[103]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[13]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[14]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[15]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[182]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[210]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[213]; - break; - } - - case '5': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[133]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[159]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[68]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[79]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[88]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[78]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[17]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[18]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[19]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[65]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[183]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[191]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[193]; - break; - } - - case '6': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[134]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[163]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[171]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[96]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[105]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[123]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[125]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[20]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[21]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[22]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[24]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[184]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[76]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[201]; - break; - } - - case '7': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[139]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[161]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[164]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[113]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[81]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[84]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[25]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[26]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[27]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[62]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[185]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[207]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[221]; - break; - } - - case '8': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[142]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[170]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[116]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[117]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[74]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[82]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[28]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[29]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[30]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[31]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[32]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[186]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[195]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[212]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[190]; - break; - } - - case '9': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[143]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[162]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[174]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[120]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[102]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[107]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[33]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[34]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[35]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[36]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[37]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[80]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[209]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[196]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[211]; - if (rds.ECC == 244) rds.ECCtext = ECCtext[190]; - break; - } - - case 'A': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[131]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[144]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[166]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[85]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[97]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[121]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[70]; - if (rds.ECC == 212) rds.ECCtext = ECCtext[86]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[38]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[39]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[40]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[41]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[60]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[178]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[215]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[214]; - break; - } - - case 'B': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[137]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[145]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[167]; - if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[101]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[109]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[100]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[42]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[43]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[44]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[61]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[66]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[206]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[188]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[203]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[93]; - if (rds.ECC == 244) rds.ECCtext = ECCtext[204]; - break; - } - - case 'C': { - if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[223]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[140]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[146]; - if (rds.ECC == 165) rds.ECCtext = ECCtext[169]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[110]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[108]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[111]; - if (rds.ECC == 211) rds.ECCtext = ECCtext[87]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[45]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[46]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[47]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[48]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[118]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[190]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[216]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[194]; - break; - } - - case 'D': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[225]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[150]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[154]; - if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[90]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[89]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[0]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[49]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[23]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[122]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[198]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[217]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[128]; - break; - } - - case 'E': { - if (rds.ECC == 160) rds.ECCtext = ECCtext[226]; - if (rds.ECC == 161) rds.ECCtext = ECCtext[138]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[151]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[172]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[175]; - if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[99]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[115]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[94]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[50]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[51]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[52]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[53]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[64]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[98]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[199]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[208]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[205]; - break; - } - - case 'F': { - if (rds.ECC == 161) rds.ECCtext = ECCtext[149]; - if (rds.ECC == 162) rds.ECCtext = ECCtext[222]; - if (rds.ECC == 163) rds.ECCtext = ECCtext[153]; - if (rds.ECC == 164) rds.ECCtext = ECCtext[176]; - if (rds.ECC == 165) rds.ECCtext = ECCtext[158]; - if (rds.ECC == 166) rds.ECCtext = ECCtext[168]; - if (rds.ECC == 208) rds.ECCtext = ECCtext[67]; - if (rds.ECC == 209) rds.ECCtext = ECCtext[75]; - if (rds.ECC == 210) rds.ECCtext = ECCtext[114]; - if (rds.ECC == 224) rds.ECCtext = ECCtext[54]; - if (rds.ECC == 225) rds.ECCtext = ECCtext[55]; - if (rds.ECC == 226) rds.ECCtext = ECCtext[56]; - if (rds.ECC == 227) rds.ECCtext = ECCtext[57]; - if (rds.ECC == 228) rds.ECCtext = ECCtext[58]; - if (rds.ECC == 240) rds.ECCtext = ECCtext[202]; - if (rds.ECC == 241) rds.ECCtext = ECCtext[192]; - if (rds.ECC == 242) rds.ECCtext = ECCtext[220]; - if (rds.ECC == 243) rds.ECCtext = ECCtext[71]; - break; - } - default: { - rds.ECCtext = ""; - break; - } - } - } - - if (((rds.rdsC >> 12) & 0x07) == 3 && rdsblock == RDS_GROUP_1A) { // LIC code readout - rds.LIC = rds.rdsC & 0xff; - rds.hasLIC = true; - if (rds.LIC > 0 && rds.LIC < 128) rds.LICtext = LICtext[rds.LIC]; else rds.LICtext = ""; - } - } - - if (rds.rdsC >> 12 == 1 && rdsblock == RDS_GROUP_1A) rds.hasTMC = true; // TMC flag - - if (!rdsDerrorThreshold) { - if (rds.rdsD != 0) { // PIN decoder - if ((rds.rdsD & 0x3f) < 61 && ((rds.rdsD >> 6) & 0x1f) < 24) { - rds.hasPIN = true; - rds.pinMin = rds.rdsD & 0x3f; - rds.pinHour = rds.rdsD >> 6 & 0x1f; - rds.pinDay = rds.rdsD >> 11 & 0x1f; - } - } - } - } break; - - case RDS_GROUP_2A: { - if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold)) { - // RT decoder (64 characters) - rds.hasRT = true; - rds.rtAB = (bitRead(rds.rdsB, 4)); // Get AB flag - - if (initab) { - rtABold = rds.rtAB; - initab = false; - } - - byte endmarkerRT64 = 64; - for (byte i = 0; i < endmarkerRT64; i++) { - if (rt_buffer[i] == 0x0d) { - endmarkerRT64 = i; - break; - } - } - - if (rds.rtAB != rtABold) { // Erase old RT, because of AB change - initrt = false; - if (rds.rtbuffer) { - char rt_buffer_temp[129]; - bool found = false; - strcpy(rt_buffer_temp, rt_buffer); - - for (int i = 0; i < 129; i++) { - if (rt_buffer_temp[i] == 0x0D) { - found = true; - } - if (found) { - rt_buffer_temp[i] = '\0'; - } - } - - wchar_t RTtext[65] = L""; // Create 16 bit char buffer for Extended ASCII - RDScharConverter(rt_buffer_temp, RTtext, sizeof(RTtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII - rds.stationText = convertToUTF8(RTtext); // Convert RDS characterset to ASCII - rds.stationText = extractUTF8Substring(rds.stationText, 0, endmarkerRT64, true); // Make sure RT does not exceed 64 characters - rds.stationText = trimTrailingSpaces(rds.stationText); // Trim empty spaces at the end - } - - for (byte i = 0; i < 64; i++) { - rt_buffer[i] = 0x20; - } - rt_buffer[64] = '\0'; - rtABold = rds.rtAB; - } - - offset = (rds.rdsB & 0xf) * 4; // Get RT character segment - rt_buffer[offset + 0] = rds.rdsC >> 8; // First character of segment - rt_buffer[offset + 1] = rds.rdsC & 0xff; // Second character of segment - rt_buffer[offset + 2] = rds.rdsD >> 8; // Thirth character of segment - rt_buffer[offset + 3] = rds.rdsD & 0xff; // Fourth character of segment - - - if (initrt || !rds.rtbuffer) { + if (rds.rtAB != rtABold) { // Erase old RT, because of AB change + initrt = false; + if (rds.rtbuffer) { char rt_buffer_temp[129]; bool found = false; strcpy(rt_buffer_temp, rt_buffer); @@ -1234,39 +1200,24 @@ void TEF6686::readRDS(byte showrdserrors) { rds.stationText = trimTrailingSpaces(rds.stationText); // Trim empty spaces at the end } - for (int i = 0; i < 64; i++) rt_buffer2[i] = rt_buffer[i]; + for (byte i = 0; i < 64; i++) { + rt_buffer[i] = 0x20; + } + rt_buffer[64] = '\0'; + rtABold = rds.rtAB; } - } break; - case RDS_GROUP_2B: { - if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsDerrorThreshold)) { - // RT decoder (32 characters) - rds.hasRT = true; - rds.rtAB32 = (bitRead(rds.rdsB, 4)); // Get AB flag + offset = (rds.rdsB & 0xf) * 4; // Get RT character segment + rt_buffer[offset + 0] = rds.rdsC >> 8; // First character of segment + rt_buffer[offset + 1] = rds.rdsC & 0xff; // Second character of segment + rt_buffer[offset + 2] = rds.rdsD >> 8; // Thirth character of segment + rt_buffer[offset + 3] = rds.rdsD & 0xff; // Fourth character of segment - if (rds.rtAB32 != rtAB32old) { // Erase old RT, because of AB change - for (byte i = 0; i < 33; i++) { - rt_buffer32[i] = 0x20; - } - rt_buffer32[32] = '\0'; - rtAB32old = rds.rtAB32; - } - - offset = (rds.rdsB & 0xf) * 2; // Get RT character segment - rt_buffer32[offset + 0] = rds.rdsD >> 8; // First character of segment - rt_buffer32[offset + 1] = rds.rdsD & 0xff; // Second character of segment - - byte endmarkerRT32 = 32; - for (byte i = 0; i < endmarkerRT32; i++) { - if (rt_buffer[i] == 0x0d) { - endmarkerRT32 = i; - break; - } - } + if (initrt || !rds.rtbuffer) { char rt_buffer_temp[129]; bool found = false; - strcpy(rt_buffer_temp, rt_buffer32); + strcpy(rt_buffer_temp, rt_buffer); for (int i = 0; i < 129; i++) { if (rt_buffer_temp[i] == 0x0D) { @@ -1277,374 +1228,422 @@ void TEF6686::readRDS(byte showrdserrors) { } } - wchar_t RTtext[33] = L""; // Create 16 bit char buffer for Extended ASCII - RDScharConverter(rt_buffer_temp, RTtext, sizeof(RTtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII - rds.stationText32 = convertToUTF8(RTtext); // Convert RDS characterset to ASCII - rds.stationText32 = extractUTF8Substring(rds.stationText32, 0, endmarkerRT32, true);// Make sure RT does not exceed 32 characters - rds.stationText = trimTrailingSpaces(rds.stationText); // Trim empty spaces at the end - } - } break; - - case RDS_GROUP_3A: { - if (!rdsDerrorThreshold) { - if (rds.rdsD != 0) rds.hasAID = true; - - bool isValuePresent = false; - for (int i = 0; i < 10; i++) { - if (rds.aid[i] == rds.rdsD) { - isValuePresent = true; - break; - } - } - - if (!isValuePresent) { - rds.aid[rds.aid_counter] = rds.rdsD; - rds.aid_counter++; - } - - if (rds.rdsD == 0xCD46) { // Check for TMC application - rds.hasTMC = true; // TMC flag - } - - // RT+ init - if (rds.rdsD == 0x4BD7) { // Check for RT+ application - rds.hasRDSplus = true; // Set flag - rtplusblock = ((rds.rdsB & 0x1F) >> 1) * 2; // Get RT+ Block - } - - if (rds.rdsD == 0x0093) { // Check for DAB+ AF application - rds.hasDABAF = true; // Set flag - DABAFblock = ((rds.rdsB & 0x1F) >> 1) * 2; // Get DAB AF Block - } - - if (rds.rdsD == 0x6552) { // Check for Enhanced RT application - _hasEnhancedRT = true; // Set flag - eRTblock = ((rds.rdsB & 0x1F) >> 1) * 2; // Get eRT block - eRTcoding = bitRead(rds.rdsC, 0); // 0 = UCS-2, 1 = UTF-8 - } - } - } break; - - case RDS_GROUP_4A: { - if (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { - // CT - uint32_t mjd; - mjd = (rds.rdsB & 0x03); - mjd <<= 15; - mjd += ((rds.rdsC >> 1) & 0x7FFF); - - long J, C, Y, M; - J = mjd + 2400001 + 68569; - C = 4 * J / 146097; - J = J - (146097 * C + 3) / 4; - Y = 4000 * (J + 1) / 1461001; - J = J - 1461 * Y / 4 + 31; - M = 80 * (J + 0) / 2447; - - if ((J - 2447 * M / 80) < 32) rds.day = J - 2447 * M / 80; - J = M / 11; - - if ((M + 2 - (12 * J)) < 13) rds.month = M + 2 - (12 * J); - if ((100 * (C - 49) + Y + J) > 2022) rds.year = 100 * (C - 49) + Y + J; - if ((((rds.rdsD >> 12) & 0x0f)) < 25) { - rds.hour = ((rds.rdsD >> 12) & 0x0f); - rds.hour += ((rds.rdsC << 4) & 0x0010); - rds.offset = ((bitRead(rds.rdsD, 5) ? -rds.rdsD & 0x3f : rds.rdsD & 0x3f) / 2); - if (bitRead(rds.rdsD, 5) & 0x3f) rds.hour -= rds.offset; else rds.hour += rds.offset; - rds.hour = (((byte)rds.hour + 24) % 24); - } - if (((rds.rdsD >> 6) & 0x3f) < 60) rds.minute = (rds.rdsD >> 6) & 0x3f; - rds.hasCT = true; - } - } break; - - case RDS_GROUP_10A: { - if (!rdsCerrorThreshold && !rdsDerrorThreshold) { - // PTYN - offset = bitRead(rds.rdsB, 0); // Get char offset - if (rds.rdsC != 0 && rds.rdsD != 0) { - ptyn_buffer[(offset * 4) + 0] = rds.rdsC >> 8; // Get position 1 and 5 - ptyn_buffer[(offset * 4) + 1] = rds.rdsC & 0xFF; // Get position 2 and 6 - ptyn_buffer[(offset * 4) + 2] = rds.rdsD >> 8; // Get position 3 and 7 - ptyn_buffer[(offset * 4) + 3] = rds.rdsD & 0xFF; // Get position 4 and 8 - for (byte i = 0; i < 8; i++) PTYNtext[i] = L'\0'; - RDScharConverter(ptyn_buffer, PTYNtext, sizeof(PTYNtext) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII - String utf8String = convertToUTF8(PTYNtext); // Convert RDS characterset to ASCII - rds.PTYN = extractUTF8Substring(utf8String, 0, 8, false); // Make sure text is not longer than 8 chars - rds.hasPTYN = true; - } - } - } break; - - case RDS_GROUP_5A: - case RDS_GROUP_6A: - case RDS_GROUP_7A: - case RDS_GROUP_8A: - case RDS_GROUP_9A: - case RDS_GROUP_11A: - case RDS_GROUP_12A: - case RDS_GROUP_13A: { - // RT+ decoding - if ((!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) && rtplusblock == rdsblock && rds.hasRDSplus) { - rds.rdsplusTag1 = ((rds.rdsB & 0x07) << 3) + (rds.rdsC >> 13); // Are we in the right RT+ block and is all ok to go? - rds.rdsplusTag2 = ((rds.rdsC & 0x01) << 5) + (rds.rdsD >> 11); - uint16_t start_marker_1 = (rds.rdsC >> 7) & 0x3F; - uint16_t length_marker_1 = (rds.rdsC >> 1) & 0x3F; - uint16_t start_marker_2 = (rds.rdsD >> 5) & 0x3F; - uint16_t length_marker_2 = (rds.rdsD & 0x1F); - togglebit = bitRead(lowByte(rds.rdsB), 4); - runningbit = bitRead(lowByte(rds.rdsB), 3); - - switch (rds.rdsplusTag1) { - case 0: rds.rdsplusTag1 = 169; break; - case 1 ... 53: rds.rdsplusTag1 += 111; break; - case 59 ... 63: rds.rdsplusTag1 += 105; break; - default: rds.rdsplusTag1 = 169; break; - } - - switch (rds.rdsplusTag2) { - case 0: rds.rdsplusTag2 = 169; break; - case 1 ... 53: rds.rdsplusTag2 += 111; break; - case 59 ... 63: rds.rdsplusTag2 += 105; break; - default: rds.rdsplusTag2 = 169; break; - } - - if (togglebit) { - for (int i = 0; i < 44; i++) { - RDSplus1[i] = 0x20; - RDSplus2[i] = 0x20; - } - RDSplus1[44] = 0; - RDSplus2[44] = 0; - } - - if (rds.rtAB == rtABold) { - for (int i = 0; i <= length_marker_1; i++)RDSplus1[i] = rt_buffer2[i + start_marker_1]; - RDSplus1[length_marker_1 + 1] = 0; - - for (int i = 0; i <= length_marker_2; i++)RDSplus2[i] = rt_buffer2[i + start_marker_2]; - RDSplus2[length_marker_2 + 1] = 0; - } - - wchar_t RTtext1[45] = L""; // Create 16 bit char buffer for Extended ASCII - RDScharConverter(RDSplus1, RTtext1, sizeof(RTtext1) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII - rds.RTContent1 = convertToUTF8(RTtext1); // Convert RDS characterset to ASCII - rds.RTContent1 = extractUTF8Substring(rds.RTContent1, 0, 44, false); // Make sure RT does not exceed 32 characters - - wchar_t RTtext2[45] = L""; // Create 16 bit char buffer for Extended ASCII - RDScharConverter(RDSplus2, RTtext2, sizeof(RTtext2) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII - rds.RTContent2 = convertToUTF8(RTtext2); // Convert RDS characterset to ASCII - rds.RTContent2 = extractUTF8Substring(rds.RTContent2, 0, 44, false); // Make sure RT does not exceed 32 characters + wchar_t RTtext[65] = L""; // Create 16 bit char buffer for Extended ASCII + RDScharConverter(rt_buffer_temp, RTtext, sizeof(RTtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII + rds.stationText = convertToUTF8(RTtext); // Convert RDS characterset to ASCII + rds.stationText = extractUTF8Substring(rds.stationText, 0, endmarkerRT64, true); // Make sure RT does not exceed 64 characters + rds.stationText = trimTrailingSpaces(rds.stationText); // Trim empty spaces at the end } - // eRT decoding - if ((!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) && eRTblock == rdsblock && _hasEnhancedRT) { - offset = (rds.rdsB & 0x1f) * 4; - eRT_buffer[offset + 0] = rds.rdsC >> 8; // First character of segment - eRT_buffer[offset + 1] = rds.rdsC & 0xff; // Second character of segment - eRT_buffer[offset + 2] = rds.rdsD >> 8; // Thirth character of segment - eRT_buffer[offset + 3] = rds.rdsD & 0xff; // Fourth character of segment - eRT_buffer[127] = '\0'; + for (int i = 0; i < 64; i++) rt_buffer2[i] = rt_buffer[i]; + } + } break; - byte endmarkereRT = 127; - bool foundendmarker = false; - for (byte i = 0; i < endmarkereRT; i++) { - if (eRT_buffer[i] == 0x0d) { - foundendmarker = true; - endmarkereRT = i; - break; - } + case RDS_GROUP_2B: { + if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsDerrorThreshold)) { + // RT decoder (32 characters) + rds.hasRT = true; + rds.rtAB32 = (bitRead(rds.rdsB, 4)); // Get AB flag + + if (rds.rtAB32 != rtAB32old) { // Erase old RT, because of AB change + for (byte i = 0; i < 33; i++) { + rt_buffer32[i] = 0x20; } + rt_buffer32[32] = '\0'; + rtAB32old = rds.rtAB32; + } - if (offset == 0 || foundendmarker) { - if (eRTcoding) rds.enhancedRTtext = eRT_buffer; else rds.enhancedRTtext = ucs2ToUtf8(eRT_buffer); // Convert to UTF-8 or UCS-2 - rds.enhancedRTtext = trimTrailingSpaces(rds.enhancedRTtext); - if (rds.enhancedRTtext.length() > 0) rds.hasEnhancedRT = true; + offset = (rds.rdsB & 0xf) * 2; // Get RT character segment + rt_buffer32[offset + 0] = rds.rdsD >> 8; // First character of segment + rt_buffer32[offset + 1] = rds.rdsD & 0xff; // Second character of segment + + byte endmarkerRT32 = 32; + for (byte i = 0; i < endmarkerRT32; i++) { + if (rt_buffer[i] == 0x0d) { + endmarkerRT32 = i; + break; } } + char rt_buffer_temp[129]; + bool found = false; + strcpy(rt_buffer_temp, rt_buffer32); - if (!rdsBerrorThreshold && rdsblock == 16 && (bitRead(rds.rdsB, 15))) rds.hasTMC = true; // TMC flag - - if ((!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) && DABAFblock == rdsblock && rds.hasDABAF) { - rds.dabaffreq = (rds.rdsC * 16); - - for (size_t i = 0; i < sizeof(DABfrequencyTable) / sizeof(DABfrequencyTable[0]); ++i) { - if (DABfrequencyTable[i].frequency == rds.dabaffreq) strcpy(rds.dabafchannel, DABfrequencyTable[i].label); + for (int i = 0; i < 129; i++) { + if (rt_buffer_temp[i] == 0x0D) { + found = true; } - - rds.dabafeid[0] = (rds.rdsD >> 12) & 0xF; - rds.dabafeid[1] = (rds.rdsD >> 8) & 0xF; - rds.dabafeid[2] = (rds.rdsD >> 4) & 0xF; - rds.dabafeid[3] = rds.rdsD & 0xF; - for (int i = 0; i < 4; i++) { - if (rds.dabafeid[i] < 10) { - rds.dabafeid[i] += '0'; // Add ASCII offset for decimal digits - } else { - rds.dabafeid[i] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F - } + if (found) { + rt_buffer_temp[i] = '\0'; } - rds.dabafeid[4] = 0; + } + + wchar_t RTtext[33] = L""; // Create 16 bit char buffer for Extended ASCII + RDScharConverter(rt_buffer_temp, RTtext, sizeof(RTtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII + rds.stationText32 = convertToUTF8(RTtext); // Convert RDS characterset to ASCII + rds.stationText32 = extractUTF8Substring(rds.stationText32, 0, endmarkerRT32, true);// Make sure RT does not exceed 32 characters + rds.stationText = trimTrailingSpaces(rds.stationText); // Trim empty spaces at the end + } + } break; + + case RDS_GROUP_3A: { + if (!rdsDerrorThreshold) { + if (rds.rdsD != 0) rds.hasAID = true; + + bool isValuePresent = false; + for (int i = 0; i < 10; i++) { + if (rds.aid[i] == rds.rdsD) { + isValuePresent = true; + break; + } + } + + if (!isValuePresent) { + rds.aid[rds.aid_counter] = rds.rdsD; + rds.aid_counter++; + } + + if (rds.rdsD == 0xCD46) { // Check for TMC application + rds.hasTMC = true; // TMC flag + } + + // RT+ init + if (rds.rdsD == 0x4BD7) { // Check for RT+ application + rds.hasRDSplus = true; // Set flag + rtplusblock = ((rds.rdsB & 0x1F) >> 1) * 2; // Get RT+ Block + } + + if (rds.rdsD == 0x0093) { // Check for DAB+ AF application + rds.hasDABAF = true; // Set flag + DABAFblock = ((rds.rdsB & 0x1F) >> 1) * 2; // Get DAB AF Block + } + + if (rds.rdsD == 0x6552) { // Check for Enhanced RT application + _hasEnhancedRT = true; // Set flag + eRTblock = ((rds.rdsB & 0x1F) >> 1) * 2; // Get eRT block + eRTcoding = bitRead(rds.rdsC, 0); // 0 = UCS-2, 1 = UTF-8 } } - break; + } break; - case RDS_GROUP_14A: { - // EON - if (!rdsAerrorThreshold && !rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { - rds.hasEON = true; // Group is there, so we have EON + case RDS_GROUP_4A: { + if (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { + // CT + uint32_t mjd; + mjd = (rds.rdsB & 0x03); + mjd <<= 15; + mjd += ((rds.rdsC >> 1) & 0x7FFF); - bool isValuePresent = false; - int eonIndex = -1; - int i = 0; - for (; i < 20; i++) { - if (eon[i].pi == rds.rdsD || rds.rdsA == rds.rdsD) { // Check if EON is already in array - isValuePresent = true; - eonIndex = i; - break; - } + long J, C, Y, M; + J = mjd + 2400001 + 68569; + C = 4 * J / 146097; + J = J - (146097 * C + 3) / 4; + Y = 4000 * (J + 1) / 1461001; + J = J - 1461 * Y / 4 + 31; + M = 80 * (J + 0) / 2447; + + if ((J - 2447 * M / 80) < 32) rds.day = J - 2447 * M / 80; + J = M / 11; + + if ((M + 2 - (12 * J)) < 13) rds.month = M + 2 - (12 * J); + if ((100 * (C - 49) + Y + J) > 2022) rds.year = 100 * (C - 49) + Y + J; + if ((((rds.rdsD >> 12) & 0x0f)) < 25) { + rds.hour = ((rds.rdsD >> 12) & 0x0f); + rds.hour += ((rds.rdsC << 4) & 0x0010); + rds.offset = ((bitRead(rds.rdsD, 5) ? -rds.rdsD & 0x3f : rds.rdsD & 0x3f) / 2); + if (bitRead(rds.rdsD, 5) & 0x3f) rds.hour -= rds.offset; else rds.hour += rds.offset; + rds.hour = (((byte)rds.hour + 24) % 24); + } + if (((rds.rdsD >> 6) & 0x3f) < 60) rds.minute = (rds.rdsD >> 6) & 0x3f; + rds.hasCT = true; + } + } break; + + case RDS_GROUP_10A: { + if (!rdsCerrorThreshold && !rdsDerrorThreshold) { + // PTYN + offset = bitRead(rds.rdsB, 0); // Get char offset + if (rds.rdsC != 0 && rds.rdsD != 0) { + ptyn_buffer[(offset * 4) + 0] = rds.rdsC >> 8; // Get position 1 and 5 + ptyn_buffer[(offset * 4) + 1] = rds.rdsC & 0xFF; // Get position 2 and 6 + ptyn_buffer[(offset * 4) + 2] = rds.rdsD >> 8; // Get position 3 and 7 + ptyn_buffer[(offset * 4) + 3] = rds.rdsD & 0xFF; // Get position 4 and 8 + for (byte i = 0; i < 8; i++) PTYNtext[i] = L'\0'; + RDScharConverter(ptyn_buffer, PTYNtext, sizeof(PTYNtext) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII + String utf8String = convertToUTF8(PTYNtext); // Convert RDS characterset to ASCII + rds.PTYN = extractUTF8Substring(utf8String, 0, 8, false); // Make sure text is not longer than 8 chars + rds.hasPTYN = true; + } + } + } break; + + case RDS_GROUP_5A: + case RDS_GROUP_6A: + case RDS_GROUP_7A: + case RDS_GROUP_8A: + case RDS_GROUP_9A: + case RDS_GROUP_11A: + case RDS_GROUP_12A: + case RDS_GROUP_13A: { + // RT+ decoding + if ((!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) && rtplusblock == rdsblock && rds.hasRDSplus) { + rds.rdsplusTag1 = ((rds.rdsB & 0x07) << 3) + (rds.rdsC >> 13); // Are we in the right RT+ block and is all ok to go? + rds.rdsplusTag2 = ((rds.rdsC & 0x01) << 5) + (rds.rdsD >> 11); + uint16_t start_marker_1 = (rds.rdsC >> 7) & 0x3F; + uint16_t length_marker_1 = (rds.rdsC >> 1) & 0x3F; + uint16_t start_marker_2 = (rds.rdsD >> 5) & 0x3F; + uint16_t length_marker_2 = (rds.rdsD & 0x1F); + togglebit = bitRead(lowByte(rds.rdsB), 4); + runningbit = bitRead(lowByte(rds.rdsB), 3); + + switch (rds.rdsplusTag1) { + case 0: rds.rdsplusTag1 = 169; break; + case 1 ... 53: rds.rdsplusTag1 += 111; break; + case 59 ... 63: rds.rdsplusTag1 += 105; break; + default: rds.rdsplusTag1 = 169; break; + } + + switch (rds.rdsplusTag2) { + case 0: rds.rdsplusTag2 = 169; break; + case 1 ... 53: rds.rdsplusTag2 += 111; break; + case 59 ... 63: rds.rdsplusTag2 += 105; break; + default: rds.rdsplusTag2 = 169; break; + } + + if (togglebit) { + for (int i = 0; i < 44; i++) { + RDSplus1[i] = 0x20; + RDSplus2[i] = 0x20; } + RDSplus1[44] = 0; + RDSplus2[44] = 0; + } - if (!isValuePresent) { - eon[eon_counter].picode[0] = (rds.rdsD >> 12) & 0xF; - eon[eon_counter].picode[1] = (rds.rdsD >> 8) & 0xF; - eon[eon_counter].picode[2] = (rds.rdsD >> 4) & 0xF; - eon[eon_counter].picode[3] = rds.rdsD & 0xF; - for (int j = 0; j < 4; j++) { - if (eon[eon_counter].picode[j] < 10) { - eon[eon_counter].picode[j] += '0'; // Add ASCII offset for decimal digits - } else { - eon[eon_counter].picode[j] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F - } - } + if (rds.rtAB == rtABold) { + for (int i = 0; i <= length_marker_1; i++)RDSplus1[i] = rt_buffer2[i + start_marker_1]; + RDSplus1[length_marker_1 + 1] = 0; - eon[eon_counter].pi = rds.rdsD; // Store PI on next array - if (eon_counter < 20) eon_counter++; - eonIndex = eon_counter - 1; + for (int i = 0; i <= length_marker_2; i++)RDSplus2[i] = rt_buffer2[i + start_marker_2]; + RDSplus2[length_marker_2 + 1] = 0; + } + + wchar_t RTtext1[45] = L""; // Create 16 bit char buffer for Extended ASCII + RDScharConverter(RDSplus1, RTtext1, sizeof(RTtext1) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII + rds.RTContent1 = convertToUTF8(RTtext1); // Convert RDS characterset to ASCII + rds.RTContent1 = extractUTF8Substring(rds.RTContent1, 0, 44, false); // Make sure RT does not exceed 32 characters + + wchar_t RTtext2[45] = L""; // Create 16 bit char buffer for Extended ASCII + RDScharConverter(RDSplus2, RTtext2, sizeof(RTtext2) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII + rds.RTContent2 = convertToUTF8(RTtext2); // Convert RDS characterset to ASCII + rds.RTContent2 = extractUTF8Substring(rds.RTContent2, 0, 44, false); // Make sure RT does not exceed 32 characters + } + + // eRT decoding + if ((!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) && eRTblock == rdsblock && _hasEnhancedRT) { + offset = (rds.rdsB & 0x1f) * 4; + eRT_buffer[offset + 0] = rds.rdsC >> 8; // First character of segment + eRT_buffer[offset + 1] = rds.rdsC & 0xff; // Second character of segment + eRT_buffer[offset + 2] = rds.rdsD >> 8; // Thirth character of segment + eRT_buffer[offset + 3] = rds.rdsD & 0xff; // Fourth character of segment + eRT_buffer[127] = '\0'; + + byte endmarkereRT = 127; + bool foundendmarker = false; + for (byte i = 0; i < endmarkereRT; i++) { + if (eRT_buffer[i] == 0x0d) { + foundendmarker = true; + endmarkereRT = i; + break; + } + } + + if (offset == 0 || foundendmarker) { + if (eRTcoding) rds.enhancedRTtext = eRT_buffer; else rds.enhancedRTtext = ucs2ToUtf8(eRT_buffer); // Convert to UTF-8 or UCS-2 + rds.enhancedRTtext = trimTrailingSpaces(rds.enhancedRTtext); + if (rds.enhancedRTtext.length() > 0) rds.hasEnhancedRT = true; + } + } + + + if (!rdsBerrorThreshold && rdsblock == 16 && (bitRead(rds.rdsB, 15))) rds.hasTMC = true; // TMC flag + + if ((!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) && DABAFblock == rdsblock && rds.hasDABAF) { + rds.dabaffreq = (rds.rdsC * 16); + + for (size_t i = 0; i < sizeof(DABfrequencyTable) / sizeof(DABfrequencyTable[0]); ++i) { + if (DABfrequencyTable[i].frequency == rds.dabaffreq) strcpy(rds.dabafchannel, DABfrequencyTable[i].label); + } + + rds.dabafeid[0] = (rds.rdsD >> 12) & 0xF; + rds.dabafeid[1] = (rds.rdsD >> 8) & 0xF; + rds.dabafeid[2] = (rds.rdsD >> 4) & 0xF; + rds.dabafeid[3] = rds.rdsD & 0xF; + for (int i = 0; i < 4; i++) { + if (rds.dabafeid[i] < 10) { + rds.dabafeid[i] += '0'; // Add ASCII offset for decimal digits } else { + rds.dabafeid[i] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F + } + } + rds.dabafeid[4] = 0; + } + } + break; + + case RDS_GROUP_14A: { + // EON + if (!rdsAerrorThreshold && !rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold) { + rds.hasEON = true; // Group is there, so we have EON + + bool isValuePresent = false; + int eonIndex = -1; + int i = 0; + for (; i < 20; i++) { + if (eon[i].pi == rds.rdsD || rds.rdsA == rds.rdsD) { // Check if EON is already in array + isValuePresent = true; eonIndex = i; + break; } + } - offset = rds.rdsB & 0x0F; // Read offset - - if (offset < 4 && eon[eonIndex].pi == rds.rdsD) { - eon_buffer[eonIndex][(offset * 2) + 0] = rds.rdsC >> 8; // First character of segment - eon_buffer[eonIndex][(offset * 2) + 1] = rds.rdsC & 0xFF; // Second character of segment - } - - if (eon[eonIndex].pi == rds.rdsD) { // Last chars are received - RDScharConverter(eon_buffer[eonIndex], EONPStext[eonIndex], sizeof(EONPStext[eonIndex]) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII - String utf8String = convertToUTF8(EONPStext[eonIndex]); // Convert RDS characterset to ASCII - eon[eonIndex].ps = extractUTF8Substring(utf8String, 0, 8, false); // Make sure PS does not exceed 8 characters - } - - if (offset == 13 && eon[eonIndex].pi == rds.rdsD) { - eon[eonIndex].taset = true; - eon[eonIndex].ta = bitRead(rds.rdsC, 0); - eon[eonIndex].pty = (rds.rdsC >> 11) & 0xF; - } - - if (bitRead(rds.rdsB, 4) && eon[eonIndex].pi == rds.rdsD) eon[eonIndex].tp = true; - - if (offset > 4 && offset < 9 && eon[eonIndex].pi == rds.rdsD) { // Check if mapped frequency belongs to current frequency - if (((rds.rdsC >> 8) * 10 + 8750) == currentfreq && (rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) { - if (eon[eonIndex].mappedfreq == 0) { - eon[eonIndex].mappedfreq = ((rds.rdsC & 0xFF) * 10 + 8750); // Add mapped frequency to array - } else if (eon[eonIndex].mappedfreq2 == 0 && eon[eonIndex].mappedfreq != ((rds.rdsC & 0xFF) * 10 + 8750) && (rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) { - eon[eonIndex].mappedfreq2 = ((rds.rdsC & 0xFF) * 10 + 8750); - } else if (eon[eonIndex].mappedfreq3 == 0 && eon[eonIndex].mappedfreq2 != ((rds.rdsC & 0xFF) * 10 + 8750) && eon[eonIndex].mappedfreq != ((rds.rdsC & 0xFF) * 10 + 8750) && (rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) { - eon[eonIndex].mappedfreq3 = ((rds.rdsC & 0xFF) * 10 + 8750); - } + if (!isValuePresent) { + eon[eon_counter].picode[0] = (rds.rdsD >> 12) & 0xF; + eon[eon_counter].picode[1] = (rds.rdsD >> 8) & 0xF; + eon[eon_counter].picode[2] = (rds.rdsD >> 4) & 0xF; + eon[eon_counter].picode[3] = rds.rdsD & 0xF; + for (int j = 0; j < 4; j++) { + if (eon[eon_counter].picode[j] < 10) { + eon[eon_counter].picode[j] += '0'; // Add ASCII offset for decimal digits + } else { + eon[eon_counter].picode[j] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F } } - for (int i = 0; i < 20; i++) { - for (int j = 0; j < 20 - i - 1; j++) { - if (eon[j].pi == 0) continue; + eon[eon_counter].pi = rds.rdsD; // Store PI on next array + if (eon_counter < 20) eon_counter++; + eonIndex = eon_counter - 1; + } else { + eonIndex = i; + } - if (eon[j].pi > eon[j + 1].pi && eon[j + 1].pi != 0) { - std::swap(eon[j].pi, eon[j + 1].pi); - std::swap(eon[j].mappedfreq, eon[j + 1].mappedfreq); - std::swap(eon[j].mappedfreq2, eon[j + 1].mappedfreq2); - std::swap(eon[j].mappedfreq3, eon[j + 1].mappedfreq3); - char temp6[6]; - strncpy(temp6, eon[j].picode, sizeof(temp6) - 1); - temp6[sizeof(temp6) - 1] = '\0'; - strncpy(eon[j].picode, eon[j + 1].picode, sizeof(eon[j].picode) - 1); - eon[j].picode[sizeof(eon[j].picode) - 1] = '\0'; - strncpy(eon[j + 1].picode, temp6, sizeof(eon[j + 1].picode) - 1); - eon[j + 1].picode[sizeof(eon[j + 1].picode) - 1] = '\0'; - std::swap(eon[j].pty, eon[j + 1].pty); - std::swap(eon[j].ps, eon[j + 1].ps); - std::swap(eon[j].ta, eon[j + 1].ta); - std::swap(eon[j].tp, eon[j + 1].tp); - std::swap(eon[j].taset, eon[j + 1].taset); - std::swap(eon_buffer[j], eon_buffer[j + 1]); - } + offset = rds.rdsB & 0x0F; // Read offset + + if (offset < 4 && eon[eonIndex].pi == rds.rdsD) { + eon_buffer[eonIndex][(offset * 2) + 0] = rds.rdsC >> 8; // First character of segment + eon_buffer[eonIndex][(offset * 2) + 1] = rds.rdsC & 0xFF; // Second character of segment + } + + if (eon[eonIndex].pi == rds.rdsD) { // Last chars are received + RDScharConverter(eon_buffer[eonIndex], EONPStext[eonIndex], sizeof(EONPStext[eonIndex]) / sizeof(wchar_t), false); // Convert 8 bit ASCII to 16 bit ASCII + String utf8String = convertToUTF8(EONPStext[eonIndex]); // Convert RDS characterset to ASCII + eon[eonIndex].ps = extractUTF8Substring(utf8String, 0, 8, false); // Make sure PS does not exceed 8 characters + } + + if (offset == 13 && eon[eonIndex].pi == rds.rdsD) { + eon[eonIndex].taset = true; + eon[eonIndex].ta = bitRead(rds.rdsC, 0); + eon[eonIndex].pty = (rds.rdsC >> 11) & 0xF; + } + + if (bitRead(rds.rdsB, 4) && eon[eonIndex].pi == rds.rdsD) eon[eonIndex].tp = true; + + if (offset > 4 && offset < 9 && eon[eonIndex].pi == rds.rdsD) { // Check if mapped frequency belongs to current frequency + if (((rds.rdsC >> 8) * 10 + 8750) == currentfreq && (rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) { + if (eon[eonIndex].mappedfreq == 0) { + eon[eonIndex].mappedfreq = ((rds.rdsC & 0xFF) * 10 + 8750); // Add mapped frequency to array + } else if (eon[eonIndex].mappedfreq2 == 0 && eon[eonIndex].mappedfreq != ((rds.rdsC & 0xFF) * 10 + 8750) && (rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) { + eon[eonIndex].mappedfreq2 = ((rds.rdsC & 0xFF) * 10 + 8750); + } else if (eon[eonIndex].mappedfreq3 == 0 && eon[eonIndex].mappedfreq2 != ((rds.rdsC & 0xFF) * 10 + 8750) && eon[eonIndex].mappedfreq != ((rds.rdsC & 0xFF) * 10 + 8750) && (rds.rdsC >> 8) > 0 && (rds.rdsC >> 8) < 205) { + eon[eonIndex].mappedfreq3 = ((rds.rdsC & 0xFF) * 10 + 8750); + } + } + } + + for (int i = 0; i < 20; i++) { + for (int j = 0; j < 20 - i - 1; j++) { + if (eon[j].pi == 0) continue; + + if (eon[j].pi > eon[j + 1].pi && eon[j + 1].pi != 0) { + std::swap(eon[j].pi, eon[j + 1].pi); + std::swap(eon[j].mappedfreq, eon[j + 1].mappedfreq); + std::swap(eon[j].mappedfreq2, eon[j + 1].mappedfreq2); + std::swap(eon[j].mappedfreq3, eon[j + 1].mappedfreq3); + char temp6[6]; + strncpy(temp6, eon[j].picode, sizeof(temp6) - 1); + temp6[sizeof(temp6) - 1] = '\0'; + strncpy(eon[j].picode, eon[j + 1].picode, sizeof(eon[j].picode) - 1); + eon[j].picode[sizeof(eon[j].picode) - 1] = '\0'; + strncpy(eon[j + 1].picode, temp6, sizeof(eon[j + 1].picode) - 1); + eon[j + 1].picode[sizeof(eon[j + 1].picode) - 1] = '\0'; + std::swap(eon[j].pty, eon[j + 1].pty); + std::swap(eon[j].ps, eon[j + 1].ps); + std::swap(eon[j].ta, eon[j + 1].ta); + std::swap(eon[j].tp, eon[j + 1].tp); + std::swap(eon[j].taset, eon[j + 1].taset); + std::swap(eon_buffer[j], eon_buffer[j + 1]); } } } } - break; + } + break; - case RDS_GROUP_15A: { - if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold)) { - if (pslong_process && rds.stationNameLong.length() > 0) rds.hasLongPS = true; - offset = (rds.rdsB & 0xf) * 4; // Get Long PS character segment + case RDS_GROUP_15A: { + if (showrdserrors == 3 || (!rdsBerrorThreshold && !rdsCerrorThreshold && !rdsDerrorThreshold)) { + if (pslong_process && rds.stationNameLong.length() > 0) rds.hasLongPS = true; + offset = (rds.rdsB & 0xf) * 4; // Get Long PS character segment - pslong_buffer2[offset + 0] = pslong_buffer[offset + 0]; // Copy PS long buffer - pslong_buffer2[offset + 1] = pslong_buffer[offset + 1]; - pslong_buffer2[offset + 2] = pslong_buffer[offset + 2]; - pslong_buffer2[offset + 3] = pslong_buffer[offset + 3]; - pslong_buffer2[32] = '\0'; + pslong_buffer2[offset + 0] = pslong_buffer[offset + 0]; // Copy PS long buffer + pslong_buffer2[offset + 1] = pslong_buffer[offset + 1]; + pslong_buffer2[offset + 2] = pslong_buffer[offset + 2]; + pslong_buffer2[offset + 3] = pslong_buffer[offset + 3]; + pslong_buffer2[32] = '\0'; - pslong_buffer[offset + 0] = rds.rdsC >> 8; // First character of segment - pslong_buffer[offset + 1] = rds.rdsC & 0xff; // Second character of segment - pslong_buffer[offset + 2] = rds.rdsD >> 8; // Thirth character of segment - pslong_buffer[offset + 3] = rds.rdsD & 0xff; // Fourth character of segment - pslong_buffer[32] = '\0'; + pslong_buffer[offset + 0] = rds.rdsC >> 8; // First character of segment + pslong_buffer[offset + 1] = rds.rdsC & 0xff; // Second character of segment + pslong_buffer[offset + 2] = rds.rdsD >> 8; // Thirth character of segment + pslong_buffer[offset + 3] = rds.rdsD & 0xff; // Fourth character of segment + pslong_buffer[32] = '\0'; - byte endmarkerLPS = 32; - bool foundendmarker = false; - for (byte i = 0; i < endmarkerLPS; i++) { - if (pslong_buffer[i] == 0x0d) { - foundendmarker = true; - endmarkerLPS = i; - break; - } + byte endmarkerLPS = 32; + bool foundendmarker = false; + for (byte i = 0; i < endmarkerLPS; i++) { + if (pslong_buffer[i] == 0x0d) { + foundendmarker = true; + endmarkerLPS = i; + break; } + } - if ((offset == 0 || foundendmarker) && (pslong_process || !rds.fastps)) { // Last chars are received - if (strcmp(pslong_buffer, pslong_buffer2) == 0) { // When no difference between current and buffer, let's go... - pslong_process = true; - RDScharConverter(pslong_buffer, PSLongtext, sizeof(PSLongtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII - String utf8String = convertToUTF8(PSLongtext); // Convert RDS characterset to ASCII - rds.stationNameLong = extractUTF8Substring(utf8String, 0, endmarkerLPS, true); // Make sure PS Long does not exceed 32 characters - rds.stationNameLong = trimTrailingSpaces(rds.stationNameLong); - } - } - - if (!pslong_process && rds.fastps) { // Let's get 2 runs of 32 PS characters fast and without refresh - if (offset == 0) packet0long = true; - if (offset == 4) packet1long = true; - if (offset == 8) packet2long = true; - if (offset == 16) packet3long = true; - RDScharConverter(pslong_buffer, PSLongtext, sizeof(PSLongtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII - String utf8String = convertToUTF8(PSLongtext); // Convert RDS characterset to ASCII - rds.stationNameLong = extractUTF8Substring(utf8String, 0, endmarkerLPS, true); + if ((offset == 0 || foundendmarker) && (pslong_process || !rds.fastps)) { // Last chars are received + if (strcmp(pslong_buffer, pslong_buffer2) == 0) { // When no difference between current and buffer, let's go... + pslong_process = true; + RDScharConverter(pslong_buffer, PSLongtext, sizeof(PSLongtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII + String utf8String = convertToUTF8(PSLongtext); // Convert RDS characterset to ASCII + rds.stationNameLong = extractUTF8Substring(utf8String, 0, endmarkerLPS, true); // Make sure PS Long does not exceed 32 characters rds.stationNameLong = trimTrailingSpaces(rds.stationNameLong); - if ((packet0long && packet1long && packet2long && packet3long) || foundendmarker) pslong_process = true; // OK, we had one runs, now let's go the idle PS Long writing } } - } - break; - } - previous_rdsA = rds.rdsA; - previous_rdsB = rds.rdsB; - previous_rdsC = rds.rdsC; - previous_rdsD = rds.rdsD; + if (!pslong_process && rds.fastps) { // Let's get 2 runs of 32 PS characters fast and without refresh + if (offset == 0) packet0long = true; + if (offset == 4) packet1long = true; + if (offset == 8) packet2long = true; + if (offset == 16) packet3long = true; + RDScharConverter(pslong_buffer, PSLongtext, sizeof(PSLongtext) / sizeof(wchar_t), true); // Convert 8 bit ASCII to 16 bit ASCII + String utf8String = convertToUTF8(PSLongtext); // Convert RDS characterset to ASCII + rds.stationNameLong = extractUTF8Substring(utf8String, 0, endmarkerLPS, true); + rds.stationNameLong = trimTrailingSpaces(rds.stationNameLong); + if ((packet0long && packet1long && packet2long && packet3long) || foundendmarker) pslong_process = true; // OK, we had one runs, now let's go the idle PS Long writing + } + } + } + break; } + previous_rdsA = rds.rdsA; + previous_rdsB = rds.rdsB; + previous_rdsC = rds.rdsC; + previous_rdsD = rds.rdsD; } } From 5353449d734082df6eaf79423145861e1c329edc Mon Sep 17 00:00:00 2001 From: Sjef Verhoeven PE5PVB Date: Mon, 27 May 2024 23:44:49 +0200 Subject: [PATCH 11/13] Versioning --- src/language.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/language.h b/src/language.h index da7dd40..d3e21d4 100644 --- a/src/language.h +++ b/src/language.h @@ -1,7 +1,7 @@ #ifndef LANGUAGE_H #define LANGUAGE_H -#define VERSION "v2.10 RC8" +#define VERSION "v2.10 RC9" // [number of languages][number of texts] From ae13d8b72357842836ca01d8adc1a56177f8858c Mon Sep 17 00:00:00 2001 From: Sjef Verhoeven PE5PVB Date: Tue, 28 May 2024 11:36:15 +0200 Subject: [PATCH 12/13] Default setting for RDS filter to ON --- TEF6686_ESP32.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TEF6686_ESP32.ino b/TEF6686_ESP32.ino index 066adb9..eb88675 100644 --- a/TEF6686_ESP32.ino +++ b/TEF6686_ESP32.ino @@ -4080,7 +4080,7 @@ void DefaultSettings(byte userhardwaremodel) { EEPROM.writeByte(EE_BYTE_WIFI, 0); EEPROM.writeByte(EE_BYTE_SUBNETCLIENT, 1); EEPROM.writeByte(EE_BYTE_SHOWSWMIBAND, 1); - EEPROM.writeByte(EE_BYTE_RDS_FILTER, 0); + EEPROM.writeByte(EE_BYTE_RDS_FILTER, 1); EEPROM.writeByte(EE_BYTE_RDS_PIERRORS, 0); if (userhardwaremodel == BASE_ILI9341) EEPROM.writeUInt(EE_UINT16_FREQUENCY_LW, 180); else EEPROM.writeUInt(EE_UINT16_FREQUENCY_LW, 164); if (userhardwaremodel == BASE_ILI9341) EEPROM.writeUInt(EE_UINT16_FREQUENCY_MW, 540); else EEPROM.writeUInt(EE_UINT16_FREQUENCY_MW, 639); From 5f647ba4b1e272c0a259d2d7c9f2dbc60e658fee Mon Sep 17 00:00:00 2001 From: Sjef Verhoeven PE5PVB Date: Tue, 28 May 2024 12:04:31 +0200 Subject: [PATCH 13/13] Fixed RT and RDS meter/icon refresh on mode change --- TEF6686_ESP32.ino | 1 - 1 file changed, 1 deletion(-) diff --git a/TEF6686_ESP32.ino b/TEF6686_ESP32.ino index eb88675..f38473c 100644 --- a/TEF6686_ESP32.ino +++ b/TEF6686_ESP32.ino @@ -3559,7 +3559,6 @@ void doTuneMode() { } ShowTuneMode(); ShowMemoryPos(); - ShowFreq(0); EEPROM.writeByte(EE_BYTE_TUNEMODE, tunemode); EEPROM.commit(); }