Files
TEF6686_ESP32/src/TEF6686.cpp
2025-03-14 11:28:51 +01:00

2135 lines
93 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "TEF6686.h"
#include <map>
#include <Arduino.h>
#include <TimeLib.h>
#include "SPIFFS.h"
#include "constants.h"
unsigned long rdstimer = 0;
unsigned long bitStartTime = 0;
bool lastBitState = false;
uint16_t TEF6686::getBlockA(void) {
uint16_t blockA, dummy;
devTEF_Radio_Get_RDS_Status(&dummy, &blockA, &dummy, &dummy, &dummy, &dummy);
return blockA;
}
void TEF6686::TestAFEON() {
uint16_t status;
uint16_t dummy1;
uint16_t dummy2;
int8_t dummy3;
int16_t aflevel;
uint16_t afusn;
uint16_t afwam;
int16_t afoffset;
byte timing;
if (af_counter != 0) {
setMute();
for (int x = 0; x < af_counter; x++) {
timing = 0;
devTEF_Set_Cmd(TEF_FM, Cmd_Tune_To, 7, 3, af[x].frequency);
while (timing == 0 && !bitRead(timing, 15)) {
devTEF_Radio_Get_Quality_Status(&status, &aflevel, &afusn, &afwam, &afoffset, &dummy1, &dummy2, &dummy3);
timing = lowByte(status);
}
if (afoffset > -125 || afoffset < 125) {
devTEF_Set_Cmd(TEF_FM, Cmd_Tune_To, 7, 4, af[x].frequency);
delay(187);
devTEF_Radio_Get_RDS_Status(&rds.rdsStat, &rds.rdsA, &rds.rdsB, &rds.rdsC, &rds.rdsD, &rds.rdsErr);
if (rds.rdsStat & (1 << 9)) {
if ((afmethodB && rds.afreg ? (((rds.rdsA >> 8) & 0xF) > 2 && ((rds.correctPI >> 8) & 0xF) > 2 && ((rds.rdsA >> 12) & 0xF) == ((rds.correctPI >> 12) & 0xF) && (rds.rdsA & 0xFF) == (rds.correctPI & 0xFF)) || rds.rdsA == rds.correctPI : rds.rdsA == rds.correctPI)) {
af[x].checked = true;
af[x].afvalid = true;
} else {
af[x].checked = false;
af[x].afvalid = false;
}
} else {
af[x].checked = false;
}
}
}
}
devTEF_Set_Cmd(TEF_FM, Cmd_Tune_To, 7, 4, currentfreq);
setUnMute();
}
uint16_t TEF6686::TestAF() {
if (af_counter != 0) {
uint16_t status;
uint16_t dummy1;
uint16_t dummy2;
int8_t dummy3;
int16_t aflevel;
uint16_t afusn;
uint16_t afwam;
int16_t afoffset;
int16_t currentlevel;
uint16_t currentusn;
uint16_t currentwam;
int16_t currentoffset;
byte timing;
devTEF_Radio_Get_Quality_Status(&status, &currentlevel, &currentusn, &currentwam, &currentoffset, &dummy1, &dummy2, &dummy3);
devTEF_Radio_Get_RDS_Status(&rds.rdsStat, &rds.rdsA, &rds.rdsB, &rds.rdsC, &rds.rdsD, &rds.rdsErr);
for (int x = 0; x < af_counter; x++) {
timing = 0;
devTEF_Set_Cmd(TEF_FM, Cmd_Tune_To, 7, 3, af[x].frequency);
while (timing == 0 && !bitRead(timing, 15)) {
devTEF_Radio_Get_Quality_Status(&status, &aflevel, &afusn, &afwam, &afoffset, &dummy1, &dummy2, &dummy3);
timing = lowByte(status);
}
af[x].score = aflevel - afusn - afwam;
if (afoffset < -125 || afoffset > 125) af[x].score = -32767;
}
int16_t highestValue = af[0].score;
int highestIndex = 0;
for (int i = 1; i < af_counter; i++) {
if (af[i].score > highestValue) {
highestValue = af[i].score;
highestIndex = i;
}
}
if (af_counter != 0 && af[highestIndex].afvalid && af[highestIndex].score > (currentlevel - currentusn - currentwam) && (af[highestIndex].score - (currentlevel - currentusn - currentwam)) >= 70) {
devTEF_Set_Cmd(TEF_FM, Cmd_Tune_To, 7, 4, af[highestIndex].frequency);
delay(187);
devTEF_Radio_Get_RDS_Status(&rds.rdsStat, &rds.rdsA, &rds.rdsB, &rds.rdsC, &rds.rdsD, &rds.rdsErr);
if (rds.rdsStat & (1 << 9)) {
if ((afmethodB && rds.afreg ? (((rds.rdsA >> 8) & 0xF) > 2 && ((rds.correctPI >> 8) & 0xF) > 2 && ((rds.rdsA >> 12) & 0xF) == ((rds.correctPI >> 12) & 0xF) && (rds.rdsA & 0xFF) == (rds.correctPI & 0xFF)) || rds.rdsA == rds.correctPI : rds.rdsA == rds.correctPI)) {
currentfreq = af[highestIndex].frequency;
for (byte y = 0; y < 50; y++) {
af[y].frequency = 0;
af[y].score = -32767;
af[y].afvalid = true;
af[y].checked = false;
}
af_counter = 0;
} else {
af[highestIndex].afvalid = false;
devTEF_Set_Cmd(TEF_FM, Cmd_Tune_To, 7, 4, currentfreq);
}
} else {
devTEF_Set_Cmd(TEF_FM, Cmd_Tune_To, 7, 4, currentfreq);
}
}
}
return currentfreq;
}
void TEF6686::init(byte TEF) {
uint8_t bootstatus;
int xtalADC = 0;
Tuner_I2C_Init();
devTEF_APPL_Get_Operation_Status(&bootstatus);
if (bootstatus == 0) {
Tuner_Patch(TEF);
delay(50);
xtalADC = analogRead(15);
if (xtalADC < XTAL_0V_ADC + XTAL_ADC_TOL) {
Tuner_Init(tuner_init_tab9216);
log_d("TEF668X XTAL : 9.216M");
} else if (xtalADC > XTAL_1V_ADC - XTAL_ADC_TOL && xtalADC < XTAL_1V_ADC + XTAL_ADC_TOL) {
Tuner_Init(tuner_init_tab12000);
log_d("TEF668X XTAL : 12M");
} else if (xtalADC > XTAL_2V_ADC - XTAL_ADC_TOL && xtalADC < XTAL_2V_ADC + XTAL_ADC_TOL) {
Tuner_Init(tuner_init_tab55000);
log_d("TEF668X XTAL : 55M");
} else {
Tuner_Init(tuner_init_tab4000);
log_d("TEF668X XTAL : 4M");
}
power(1);
Tuner_Init(tuner_init_tab);
}
}
bool TEF6686::getIdentification(uint16_t &device, uint16_t &hw_version, uint16_t &sw_version) {
devTEF_Radio_Get_Identification(&device, &hw_version, &sw_version);
return device;
return hw_version;
return sw_version;
}
void TEF6686::setCoax(uint8_t mode) {
devTEF_Radio_Set_GPIO(mode);
}
void TEF6686::power(bool mode) {
devTEF_APPL_Set_OperationMode(mode);
if (mode == 0) devTEF_Set_Cmd(TEF_FM, Cmd_Tune_To, 7, 1, 10000);
}
void TEF6686::SetFreq(uint16_t frequency) {
devTEF_Radio_Tune_To(frequency);
currentfreq = ((frequency + 5) / 10) * 10;
currentfreq2 = frequency;
}
void TEF6686::SetFreqAM(uint16_t frequency) {
devTEF_Radio_Tune_AM (frequency);
}
void TEF6686::SetFreqAIR(uint16_t frequency) {
devTEF_Radio_Tune_AM (10700);
}
void TEF6686::setOffset(int8_t offset) {
devTEF_Radio_Set_LevelOffset(offset * 10);
}
void TEF6686::setAMOffset(int8_t offset) {
devTEF_Radio_Set_AMLevelOffset(offset * 10);
}
void TEF6686::setFMBandw(uint16_t bandwidth) {
devTEF_Radio_Set_Bandwidth(0, bandwidth * 10);
}
void TEF6686::setAMBandw(uint16_t bandwidth) {
devTEF_Radio_Set_BandwidthAM(0, bandwidth * 10);
}
void TEF6686::setAMCoChannel(uint16_t start, uint8_t level) {
if (start == 0) devTEF_Radio_Set_CoChannel_AM(0, start * 10, level); else devTEF_Radio_Set_CoChannel_AM(1, start * 10, level);
}
void TEF6686::setSoftmuteAM(uint8_t mode) {
devTEF_Radio_Set_Softmute_Max_AM(mode);
}
void TEF6686::setSoftmuteFM(uint8_t mode) {
devTEF_Radio_Set_Softmute_Max_FM(mode);
}
void TEF6686::setAMNoiseBlanker(uint16_t start) {
if (start == 0) devTEF_Radio_Set_Noiseblanker_AM(0, 1000); else devTEF_Radio_Set_Noiseblanker_AM(1, start * 10);
}
void TEF6686::setAMAttenuation(uint16_t start) {
devTEF_Radio_Set_Attenuator_AM(start * 10);
}
void TEF6686::setFMABandw() {
devTEF_Radio_Set_Bandwidth(1, 3110);
}
void TEF6686::setiMS(bool mph) {
devTEF_Radio_Set_MphSuppression(mph);
}
void TEF6686::setEQ(bool eq) {
devTEF_Radio_Set_ChannelEqualizer(eq);
}
bool TEF6686::getStereoStatus() {
uint16_t status;
bool stereo = 0;
if (1 == devTEF_Radio_Get_Stereo_Status(&status)) stereo = ((status >> 15) & 1) ? 1 : 0;
return stereo;
}
void TEF6686::setMono(bool mono) {
devTEF_Radio_Set_Stereo_Min(mono);
}
void TEF6686::setVolume(int8_t volume) {
devTEF_Audio_Set_Volume(volume);
}
void TEF6686::setMute() {
mute = true;
if (mpxmode) devTEF_Radio_Specials(0);
devTEF_Audio_Set_Mute(1);
}
void TEF6686::setUnMute() {
mute = false;
if (mpxmode) devTEF_Radio_Specials(1);
devTEF_Audio_Set_Mute(0);
}
void TEF6686::setAGC(uint8_t agc) {
devTEF_Radio_Set_RFAGC(agc);
}
void TEF6686::setAMAGC(uint8_t agc) {
devTEF_Radio_Set_AMRFAGC(agc);
}
void TEF6686::setDeemphasis(uint8_t timeconstant) {
switch (timeconstant) {
case 1: devTEF_Radio_Set_Deemphasis(500); break;
case 2: devTEF_Radio_Set_Deemphasis(750); break;
default: devTEF_Radio_Set_Deemphasis(0); break;
}
}
void TEF6686::setAudio(uint8_t audio) {
devTEF_Radio_Specials(audio);
if (audio == 0) mpxmode = false; else mpxmode = true;
}
void TEF6686::setFMSI(uint8_t mode) {
if (mode == 1) devTEF_APPL_Set_StereoImprovement(0);
if (mode == 2) devTEF_APPL_Set_StereoImprovement(1);
}
void TEF6686::setFMSI_Time(uint16_t attack, uint16_t decay) {
devTEF_APPL_Set_StereoBandBlend_Time(attack, decay);
}
void TEF6686::setFMSI_Gain(uint16_t band1, uint16_t band2, uint16_t band3, uint16_t band4) {
devTEF_APPL_Set_StereoBandBlend_Gain(band1 * 10, band2 * 10, band3 * 10, band4 * 10);
}
void TEF6686::setFMSI_Bias(int16_t band1, int16_t band2, int16_t band3, int16_t band4) {
devTEF_APPL_Set_StereoBandBlend_Bias(band1 - 250, band2 - 250, band3 - 250, band4 - 250);
}
void TEF6686::setFMNoiseBlanker(uint16_t start) {
if (start == 0) devTEF_Radio_Set_NoisBlanker(0, 1000); else devTEF_Radio_Set_NoisBlanker(1, start * 10);
}
void TEF6686::setStereoLevel(uint8_t start) {
if (start == 0) {
devTEF_Radio_Set_Stereo_Level(0, start * 10, 60);
devTEF_Radio_Set_Stereo_Noise(0, 240, 200);
devTEF_Radio_Set_Stereo_Mph(0, 240, 200);
} else {
devTEF_Radio_Set_Stereo_Level(3, start * 10, 60);
devTEF_Radio_Set_Stereo_Noise(3, 240, 200);
devTEF_Radio_Set_Stereo_Mph(3, 240, 200);
}
}
void TEF6686::setHighCutOffset(uint8_t start) {
if (start == 0) {
devTEF_Radio_Set_Highcut_Level(0, start * 10, 300);
devTEF_Radio_Set_Highcut_Noise(0, 360, 300);
devTEF_Radio_Set_Highcut_Mph(0, 360, 300);
} else {
devTEF_Radio_Set_Highcut_Level(3, start * 10, 300);
devTEF_Radio_Set_Highcut_Noise(3, 360, 300);
devTEF_Radio_Set_Highcut_Mph(3, 360, 300);
}
}
void TEF6686::setHighCutLevel(uint16_t limit) {
devTEF_Radio_Set_Highcut_Max(1, limit * 100);
}
void TEF6686::setStHiBlendLevel(uint16_t limit) {
devTEF_Radio_Set_StHiBlend_Max(1, limit * 100);
}
void TEF6686::setStHiBlendOffset(uint8_t start) {
if (start == 0) {
devTEF_Radio_Set_StHiBlend_Level(0, start * 10, 300);
devTEF_Radio_Set_StHiBlend_Noise(0, 360, 300);
devTEF_Radio_Set_StHiBlend_Mph(0, 360, 300);
} else {
devTEF_Radio_Set_StHiBlend_Level(3, start * 10, 300);
devTEF_Radio_Set_StHiBlend_Noise(3, 360, 300);
devTEF_Radio_Set_StHiBlend_Mph(3, 360, 300);
}
}
bool TEF6686::getProcessing(uint16_t &highcut, uint16_t &stereo, uint16_t &sthiblend, uint8_t &stband_1, uint8_t &stband_2, uint8_t &stband_3, uint8_t &stband_4) {
devTEF_Radio_Get_Processing_Status(&highcut, &stereo, &sthiblend, &stband_1, &stband_2, &stband_3, &stband_4);
return highcut;
return stereo;
return sthiblend;
return stband_1;
return stband_2;
return stband_3;
return stband_4;
}
bool TEF6686::getStatus(int16_t &level, uint16_t &USN, uint16_t &WAM, int16_t &offset, uint16_t &bandwidth, uint16_t &modulation, int8_t &snr) {
uint16_t status;
devTEF_Radio_Get_Quality_Status(&status, &level, &USN, &WAM, &offset, &bandwidth, &modulation, &snr);
return level;
return USN;
return WAM;
return bandwidth;
return modulation;
return snr;
}
bool TEF6686::getStatusAM(int16_t &level, uint16_t &noise, uint16_t &cochannel, int16_t &offset, uint16_t &bandwidth, uint16_t &modulation, int8_t &snr) {
devTEF_Radio_Get_Quality_Status_AM(&level, &noise, &cochannel, &offset, &bandwidth, &modulation, &snr);
return level;
return noise;
return cochannel;
return bandwidth;
return modulation;
return snr;
}
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;
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) && (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 ((piold == 0 || rds.rdsA != piold) && rds.rdsB != 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 (ps_process && ((rds.rdsErr >> 14) & 0x03) == 0) {
rds.picode[4] = ' ';
rds.picode[5] = ' ';
errorfreepi = true;
}
if (!errorfreepi) {
if (((rds.rdsErr >> 14) & 0x03) > 1) rds.picode[5] = '?'; else rds.picode[5] = ' ';
if (((rds.rdsErr >> 14) & 0x03) > 0) 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 ((rds.region == 1 ? ps_process : true) && rds.correctPI != 0 && rds.region > 0 && correctPIold != rds.correctPI) {
bool foundMatch = false;
fs::File file;
if (rds.region == 1 && 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<uint16_t>(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 is greater than 4096
if (stationID > 4096) {
// Adjust stationID based on specific conditions
if (stationID > 21671) {
if ((stationID & 0xF00U) == 0) {
stationID = ((uint16_t)(0xA0 + ((stationID & 0xF000U) >> 12)) << 8) + lowByte(stationID); // C0DE -> ACDE
} else if (lowByte(stationID) == 0) {
stationID = 0xAF00 + uint8_t(highByte(stationID)); // CD00 -> AFCD
}
}
// Check if the station has a fixed callsign for Canada
// Here we are skipping the `isFixed` and directly checking if the station is in Canada
if (!(rds.region == 3 && stationID == 0xAF00)) { // Example condition for a fixed callsign (AF00 is just an example)
// Determine prefix: 'W' or 'K' for USA, 'C' for Canada
if (rds.region == 1 || rds.region == 2) {
rds.stationID[0] = (stationID > 21671) ? 'W' : 'K';
} else if (rds.region == 3) {
rds.stationID[0] = 'C'; // Canadian stations start with 'C'
}
// Adjust stationID based on the region and PI code
if (stationID < 39247) {
stationID -= (stationID > 21671) ? 21672 : 4096;
} else {
stationID -= 4835;
}
// Decode the last 3 letters of the callsign
rds.stationID[1] = char(stationID / 676 + 'A'); // Using 'A' for readability
stationID %= 676; // Reduce stationID to fit within the last 676 options
rds.stationID[2] = char(stationID / 26 + 'A'); // Using 'A' for readability
rds.stationID[3] = char(stationID % 26 + 'A'); // Using 'A' for readability
}
}
// Validate callsign and handle faulty IDs
bool faultyID = false;
for (byte i = 0; i < 4; i++) {
if (!(rds.stationID[i] >= 'A' && rds.stationID[i] <= 'Z')) {
faultyID = true;
break;
}
}
// If any character is faulty, mark it as "Unknown"
if (faultyID) {
strcpy(rds.stationID, "Unknown");
} else {
rds.stationID[7] = '?'; // If not faulty, append '?'
}
rds.stationID[8] = '\0'; // Null terminate the callsign
}
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
switch (offset) {
case 0: if (((rds.rdsErr >> 8) & 0x03) > 1) rds.ps12error = true; else rds.ps12error = false; break;
case 1: if (((rds.rdsErr >> 8) & 0x03) > 1) rds.ps34error = true; else rds.ps34error = false; break;
case 2: if (((rds.rdsErr >> 8) & 0x03) > 1) rds.ps56error = true; else rds.ps56error = false; break;
case 3: if (((rds.rdsErr >> 8) & 0x03) > 1) rds.ps78error = true; else rds.ps78error = false; break;
}
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 && 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), (underscore > 0 ? true : false)); // Convert 8 bit ASCII to 16 bit ASCII
String utf8String = convertToUTF8(PStext); // Convert RDS characterset to ASCII
rds.stationName = extractUTF8Substring(utf8String, 0, 8, (underscore > 0 ? true : false)); // 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), (underscore > 0 ? true : false)); // Convert 8 bit ASCII to 16 bit ASCII
String utf8String = convertToUTF8(PStext); // Convert RDS characterset to ASCII
rds.stationName = extractUTF8Substring(utf8String, 0, 8, (underscore > 0 ? true : false));
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 (!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]); else 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_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), (underscore > 1 ? true : false)); // Convert 8 bit ASCII to 16 bit ASCII
rds.stationText = convertToUTF8(RTtext); // Convert RDS characterset to ASCII
rds.stationText = extractUTF8Substring(rds.stationText, 0, endmarkerRT64, (underscore > 1 ? true : false)); // 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);
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), (underscore > 1 ? true : false)); // Convert 8 bit ASCII to 16 bit ASCII
rds.stationText = convertToUTF8(RTtext); // Convert RDS characterset to ASCII
rds.stationText = extractUTF8Substring(rds.stationText, 0, endmarkerRT64, (underscore > 1 ? true : false)); // Make sure RT does not exceed 64 characters
rds.stationText = trimTrailingSpaces(rds.stationText); // Trim empty spaces at the end
}
for (int i = 0; i < 64; i++) rt_buffer2[i] = rt_buffer[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;
}
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);
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[33] = L""; // Create 16 bit char buffer for Extended ASCII
RDScharConverter(rt_buffer_temp, RTtext, sizeof(RTtext) / sizeof(wchar_t), (underscore > 1 ? true : false)); // Convert 8 bit ASCII to 16 bit ASCII
rds.stationText32 = convertToUTF8(RTtext); // Convert RDS characterset to ASCII
rds.stationText32 = extractUTF8Substring(rds.stationText32, 0, endmarkerRT32, (underscore > 1 ? true : false));// Make sure RT does not exceed 32 characters
rds.stationText32 = trimTrailingSpaces(rds.stationText32); // 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 && rds.ctupdate && (rds.PICTlock == rds.rdsA || rds.PICTlock == 0)) {
// CT
uint32_t mjd;
mjd = (rds.rdsB & 0x03);
mjd <<= 15;
mjd += ((rds.rdsC >> 1) & 0x7FFF);
uint16_t hour, minute, day = 1, month = 1, year = 2020; // Set default values for day, month, and year
int32_t timeoffset;
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) day = J - 2447 * M / 80;
J = M / 11;
if ((M + 2 - (12 * J)) < 13) month = M + 2 - (12 * J);
if ((100 * (C - 49) + Y + J) > 2022) year = 100 * (C - 49) + Y + J;
hour = ((rds.rdsD >> 12) & 0x000f);
hour += ((rds.rdsC << 4) & 0x0010);
timeoffset = rds.rdsD & 0x001f;
if (bitRead(rds.rdsD, 5)) timeoffset *= -1;
timeoffset *= 1800;
minute = (rds.rdsD & 0x0fc0) >> 6;
if (year < 2024 || hour > 23 || minute > 59 || timeoffset > 55800 || timeoffset < -55800) break;
struct tm tm;
tm.tm_year = year - 1900;
tm.tm_mon = month - 1;
tm.tm_mday = day;
tm.tm_hour = hour;
tm.tm_min = minute;
tm.tm_sec = 0;
tm.tm_isdst = -1;
time_t rdstime = mktime(&tm);
if (lastrdstime == 0) {
lastrdstime = rdstime;
lasttimeoffset = timeoffset;
}
if ((rdstime == lastrdstime + 60 && timeoffset == lasttimeoffset) || showrdserrors != 0) {
rds.hasCT = true;
rds.time = rdstime;
rds.offset = timeoffset;
} else {
rds.hasCT = false;
}
lastrdstime = rdstime;
lasttimeoffset = timeoffset;
}
} 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
} else {
eon[eon_counter].picode[j] += 'A' - 10; // Add ASCII offset for hexadecimal letters A-F
}
}
eon[eon_counter].pi = rds.rdsD; // Store PI on next array
if (eon_counter < 20) eon_counter++;
eonIndex = eon_counter - 1;
} else {
eonIndex = i;
}
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;
eon[eonIndex].ptyset = true;
}
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[j].ptyset, eon[j + 1].ptyset);
std::swap(eon_buffer[j], eon_buffer[j + 1]);
std::swap(EONPStext[j], EONPStext[j + 1]);
}
}
}
}
}
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
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';
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;
PSLongtext = pslong_buffer;
rds.stationNameLong = extractUTF8Substring(pslong_buffer, 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;
PSLongtext = pslong_buffer;
rds.stationNameLong = extractUTF8Substring(pslong_buffer, 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;
}
}
void TEF6686::clearRDS (bool fullsearchrds) {
devTEF_Radio_Set_RDS(fullsearchrds);
rds.piBuffer.clear();
rds.stationName = "";
rds.stationText = "";
rds.stationNameLong = "";
rds.stationText32 = "";
rds.RTContent1 = "";
rds.RTContent2 = "";
rds.PTYN = "";
rds.ECCtext = "";
rds.LICtext = "";
rds.stationIDtext = "";
rds.stationStatetext = "";
rds.enhancedRTtext = "";
PSLongtext = "";
uint8_t i;
for (i = 0; i < 8; i++) {
ps_buffer[i] = 0x20;
PStext[i] = L'\0';
ptyn_buffer[i] = 0x20;
PTYNtext[i] = L'\0';
}
ps_buffer[8] = 0;
ptyn_buffer[8] = 0;
PStext[8] = L'\0';
PTYNtext[8] = L'\0';
for (i = 0; i < 64; i++) rt_buffer[i] = 0x20;
rt_buffer[64] = 0;
for (i = 0; i < 128; i++) eRT_buffer[i] = 0x20;
eRT_buffer[128] = '\0';
for (i = 0; i < 32; i++) {
rt_buffer32[i] = 0x20;
pslong_buffer[i] = 0x20;
pslong_buffer2[i] = 0x20;
// PSLongtext[i] = L'\0';
}
rt_buffer32[32] = 0;
pslong_buffer[32] = 0;
// PSLongtext[32] = L'\0';
for (i = 0; i < 17; i++) rds.stationType[i] = 0x20;
rds.stationType[17] = 0;
for (i = 0; i < 6; i++) rds.picode[i] = 0x20;
rds.picode[6] = 0;
for (i = 0; i < 4; i++) rds.dabafeid[i] = 0x20;
rds.dabafeid[4] = 0;
for (i = 0; i < 3; i++) rds.dabafchannel[i] = 0x20;
rds.dabafchannel[3] = 0;
for (i = 0; i < 8; i++) rds.stationID[i] = 0x20;
rds.stationID[8] = 0;
for (i = 0; i < 2; i++) rds.stationState[i] = 0x20;
rds.stationState[2] = 0;
for (i = 0; i < 50; i++) {
af[i].frequency = 0;
af[i].score = -32767;
af[i].afvalid = true;
af[i].checked = false;
af[i].regional = false;
af[i].same = false;
}
for (i = 0; i < 20; i++) {
eon[i].pty = 254;
eon[i].pi = 0;
eon[i].ps = "";
eon[i].mappedfreq = 0;
eon[i].mappedfreq2 = 0;
eon[i].mappedfreq3 = 0;
eon[i].ta = false;
eon[i].tp = false;
eon[i].taset = false;
eon[i].ptyset = false;
for (int y = 0; y < 5; y++) {
eon[i].picode[y] = '\0';
}
for (int j = 0; j < 9; j++) {
EONPStext[i][j] = L'\0';
eon_buffer[i][j] = '\0';
}
}
for (i = 0; i < 45; i++) {
RDSplus1[i] = 0;
RDSplus2[i] = 0;
}
for (i = 0; i < 10; i++) rds.aid[i] = 0;
rdsblock = 254;
piold = 0;
rds.correctPI = 0;
rds.ECC = 254;
rds.LIC = 254;
rds.pinHour = 0;
rds.pinMin = 0;
rds.pinDay = 0;
rds.stationTypeCode = 32;
rds.dabaffreq = 0;
rds.hasPIN = false;
rds.hasECC = false;
rds.hasLIC = false;
rds.hasRT = false;
rds.hasRDS = false;
rds.hasTP = false;
rds.hasAF = false;
rds.hasTA = false;
rds.hasEON = false;
rds.hasCT = false;
rds.hasTMC = false;
rds.hasAID = false;
rds.hasPTYN = false;
rds.hasLongPS = false;
rds.hasRDSplus = false;
rds.hasDABAF = false;
rds.hasEnhancedRT = false;
rt_process = false;
ps_process = false;
pslong_process = false;
rds.rdsreset = true;
rds.hasArtificialhead = false;
rds.hasCompressed = false;
rds.hasDynamicPTY = false;
rds.hasStereo = false;
af_counter = 0;
af_updatecounter = 0;
eon_counter = 0;
afreset = true;
rds.MS = 0;
rds.rdsAerror = true;
rds.rdsBerror = true;
rds.rdsCerror = true;
rds.rdsDerror = true;
initrt = true;
initab = true;
rds.rdsplusTag1 = 169;
rds.rdsplusTag2 = 169;
afinit = false;
errorfreepi = false;
afmethodB = false;
packet0 = false;
packet1 = false;
packet2 = false;
packet3 = false;
packet0long = false;
packet1long = false;
packet2long = false;
packet3long = false;
rds.aid_counter = 0;
afmethodBprobe = false;
afmethodBtrigger = false;
correctPIold = 0;
af_number = 0;
_hasEnhancedRT = false;
rds.ps12error = true;
rds.ps34error = true;
rds.ps56error = true;
rds.ps78error = true;
}
void TEF6686::tone(uint16_t time, int16_t amplitude, uint16_t frequency) {
devTEF_Audio_Set_Mute(0);
devTEF_Radio_Set_Wavegen(1, amplitude, frequency);
delay (time);
devTEF_Radio_Set_Wavegen(0, 0, 0);
}
void TEF6686::I2Sin(bool mode) {
devTEF_Radio_Set_I2S_Input(mode);
}
String TEF6686::convertToUTF8(const wchar_t* input) {
String output;
while (*input) {
uint32_t unicode = *input;
if (unicode < 0x80) {
output += (char)unicode;
} else if (unicode < 0x800) {
output += (char)(0xC0 | (unicode >> 6));
output += (char)(0x80 | (unicode & 0x3F));
} else if (unicode < 0x10000) {
output += (char)(0xE0 | (unicode >> 12));
output += (char)(0x80 | ((unicode >> 6) & 0x3F));
output += (char)(0x80 | (unicode & 0x3F));
} else {
output += (char)(0xF0 | (unicode >> 18));
output += (char)(0x80 | ((unicode >> 12) & 0x3F));
output += (char)(0x80 | ((unicode >> 6) & 0x3F));
output += (char)(0x80 | (unicode & 0x3F));
}
input++;
}
return output;
}
String TEF6686::extractUTF8Substring(const String & utf8String, size_t start, size_t length, bool under) {
String substring;
size_t utf8Length = utf8String.length();
size_t utf8Index = 0;
size_t charIndex = 0;
while (utf8Index < utf8Length && charIndex < start + length) {
uint8_t currentByte = utf8String.charAt(utf8Index);
uint8_t numBytes = 0;
if (currentByte < 0x80) {
numBytes = 1;
} else if ((currentByte >> 5) == 0x6) {
numBytes = 2;
} else if ((currentByte >> 4) == 0xE) {
numBytes = 3;
} else if ((currentByte >> 3) == 0x1E) {
numBytes = 4;
}
if (charIndex >= start) {
substring += utf8String.substring(utf8Index, utf8Index + numBytes);
}
utf8Index += numBytes;
charIndex++;
}
if (under) {
while (substring.length() < length) {
substring += '_';
}
}
return substring;
}
void TEF6686::RDScharConverter(const char* input, wchar_t* output, size_t size, bool under) {
for (size_t i = 0; i < size - 1; i++) {
char currentChar = input[i];
switch (currentChar) {
case 0x0A: if (under) output[i] = L'_'; else output[i] = L' '; break;
case 0x20: if (under) output[i] = L'_'; else output[i] = L' '; break;
case 0x21 ... 0x5D: output[i] = static_cast<wchar_t>(currentChar); break;
case 0x5E: output[i] = L''; break;
case 0x5F: output[i] = L'_'; break;
case 0x60: output[i] = L'`'; break;
case 0x61 ... 0x7d: output[i] = static_cast<wchar_t>(currentChar); break;
case 0x7E: output[i] = L'¯'; break;
case 0x7F: output[i] = L' '; break;
case 0x80: output[i] = L'á'; break;
case 0x81: output[i] = L'à'; break;
case 0x82: output[i] = L'é'; break;
case 0x83: output[i] = L'è'; break;
case 0x84: output[i] = L'í'; break;
case 0x85: output[i] = L'ì'; break;
case 0x86: output[i] = L'ó'; break;
case 0x87: output[i] = L'ò'; break;
case 0x88: output[i] = L'ú'; break;
case 0x89: output[i] = L'ù'; break;
case 0x8A: output[i] = L'Ñ'; break;
case 0x8B: output[i] = L'Ç'; break;
case 0x8C: output[i] = L'Ş'; break;
case 0x8D: output[i] = L'β'; break;
case 0x8E: output[i] = L'¡'; break;
case 0x8F: output[i] = L'IJ'; break;
case 0x90: output[i] = L'â'; break;
case 0x91: output[i] = L'ä'; break;
case 0x92: output[i] = L'ê'; break;
case 0x93: output[i] = L'ë'; break;
case 0x94: output[i] = L'î'; break;
case 0x95: output[i] = L'ï'; break;
case 0x96: output[i] = L'ô'; break;
case 0x97: output[i] = L'ö'; break;
case 0x98: output[i] = L'û'; break;
case 0x99: output[i] = L'ü'; break;
case 0x9A: output[i] = L'ñ'; break;
case 0x9B: output[i] = L'ç'; break;
case 0x9C: output[i] = L'ş'; break;
case 0x9D: output[i] = L'ǧ'; break;
case 0x9E: output[i] = L'ı'; break;
case 0x9F: output[i] = L'ij'; break;
case 0xA0: output[i] = L'ª'; break;
case 0xA1: output[i] = L'α'; break;
case 0xA2: output[i] = L'©'; break;
case 0xA3: output[i] = L''; break;
case 0xA4: output[i] = L'Ǧ'; break;
case 0xA5: output[i] = L'ě'; break;
case 0xA6: output[i] = L'ň'; break;
case 0xA7: output[i] = L'ő'; break;
case 0xA8: output[i] = L'π'; break;
case 0xA9: output[i] = L''; break;
case 0xAA: output[i] = L'£'; break;
case 0xAB: output[i] = L'$'; break;
case 0xAC: output[i] = L''; break;
case 0xAD: output[i] = L''; break;
case 0xAE: output[i] = L''; break;
case 0xAF: output[i] = L''; break;
case 0xB0: output[i] = L'º'; break;
case 0xB1: output[i] = L'¹'; break;
case 0xB2: output[i] = L'²'; break;
case 0xB3: output[i] = L'³'; break;
case 0xB4: output[i] = L'±'; break;
case 0xB5: output[i] = L'İ'; break;
case 0xB6: output[i] = L'ń'; break;
case 0xB7: output[i] = L'ű'; break;
case 0xB8: output[i] = L'µ'; break;
case 0xB9: output[i] = L'¿'; break;
case 0xBA: output[i] = L'÷'; break;
case 0xBB: output[i] = L'°'; break;
case 0xBC: output[i] = L'¼'; break;
case 0xBD: output[i] = L'½'; break;
case 0xBE: output[i] = L'¾'; break;
case 0xBF: output[i] = L'§'; break;
case 0xC0: output[i] = L'Á'; break;
case 0xC1: output[i] = L'À'; break;
case 0xC2: output[i] = L'É'; break;
case 0xC3: output[i] = L'È'; break;
case 0xC4: output[i] = L'Í'; break;
case 0xC5: output[i] = L'Ì'; break;
case 0xC6: output[i] = L'Ó'; break;
case 0xC7: output[i] = L'Ò'; break;
case 0xC8: output[i] = L'Ú'; break;
case 0xC9: output[i] = L'Ù'; break;
case 0xCA: output[i] = L'Ř'; break;
case 0xCB: output[i] = L'Č'; break;
case 0xCC: output[i] = L'Š'; break;
case 0xCD: output[i] = L'Ž'; break;
case 0xCE: output[i] = L'Ð'; break;
case 0xCF: output[i] = L'Ŀ'; break;
case 0xD0: output[i] = L'Â'; break;
case 0xD1: output[i] = L'Ä'; break;
case 0xD2: output[i] = L'Ê'; break;
case 0xD3: output[i] = L'Ë'; break;
case 0xD4: output[i] = L'Î'; break;
case 0xD5: output[i] = L'Ï'; break;
case 0xD6: output[i] = L'Ô'; break;
case 0xD7: output[i] = L'Ö'; break;
case 0xD8: output[i] = L'Û'; break;
case 0xD9: output[i] = L'Ü'; break;
case 0xDA: output[i] = L'ř'; break;
case 0xDB: output[i] = L'č'; break;
case 0xDC: output[i] = L'š'; break;
case 0xDD: output[i] = L'ž'; break;
case 0xDE: output[i] = L'đ'; break;
case 0xDF: output[i] = L'ŀ'; break;
case 0xE0: output[i] = L'Ã'; break;
case 0xE1: output[i] = L'Å'; break;
case 0xE2: output[i] = L'Æ'; break;
case 0xE3: output[i] = L'Œ'; break;
case 0xE4: output[i] = L'ŷ'; break;
case 0xE5: output[i] = L'Ý'; break;
case 0xE6: output[i] = L'Õ'; break;
case 0xE7: output[i] = L'Ø'; break;
case 0xE8: output[i] = L'Þ'; break;
case 0xE9: output[i] = L'Ŋ'; break;
case 0xEA: output[i] = L'Ŕ'; break;
case 0xEB: output[i] = L'Ć'; break;
case 0xEC: output[i] = L'Ś'; break;
case 0xED: output[i] = L'Ź'; break;
case 0xEE: output[i] = L'Ŧ'; break;
case 0xEF: output[i] = L'ð'; break;
case 0xF0: output[i] = L'ã'; break;
case 0xF1: output[i] = L'å'; break;
case 0xF2: output[i] = L'æ'; break;
case 0xF3: output[i] = L'œ'; break;
case 0xF4: output[i] = L'ŵ'; break;
case 0xF5: output[i] = L'ý'; break;
case 0xF6: output[i] = L'õ'; break;
case 0xF7: output[i] = L'ø'; break;
case 0xF8: output[i] = L'þ'; break;
case 0xF9: output[i] = L'ŋ'; break;
case 0xFA: output[i] = L'ŕ'; break;
case 0xFB: output[i] = L'ć'; break;
case 0xFC: output[i] = L'ś'; break;
case 0xFD: output[i] = L'ź'; break;
case 0xFE: output[i] = L'ŧ'; break;
case 0xFF: output[i] = L' '; break;
}
}
output[size - 1] = L'\0';
}
String TEF6686::trimTrailingSpaces(String str) {
int end = str.length() - 1;
while (end >= 0 && isspace(str[end])) end--;
return str.substring(0, end + 1);
}
String TEF6686::ucs2ToUtf8(const char* ucs2Input) {
String utf8Output;
size_t length = 0;
while (ucs2Input[length] != '\0' || ucs2Input[length + 1] != '\0') {
length += 2;
}
for (size_t i = 0; i < length; i += 2) {
uint16_t ucs2Char = ((uint8_t)ucs2Input[i] << 8) | (uint8_t)ucs2Input[i + 1];
if (ucs2Char <= 0x7F) {
utf8Output += (char)ucs2Char;
} else if (ucs2Char <= 0x7FF) {
utf8Output += (char)(0xC0 | (ucs2Char >> 6));
utf8Output += (char)(0x80 | (ucs2Char & 0x3F));
} else {
utf8Output += (char)(0xE0 | (ucs2Char >> 12));
utf8Output += (char)(0x80 | ((ucs2Char >> 6) & 0x3F));
utf8Output += (char)(0x80 | (ucs2Char & 0x3F));
}
}
return utf8Output;
}
bool TEF6686::isFixedCallsign(uint16_t stationID, char* stationIDStr) {
for (int i = 0; i < sizeof(fixedPI) / sizeof(fixedPI[0]); i++) {
if (stationID == fixedPI[i]) {
strcpy(stationIDStr, fixedCalls[i]); // Assign fixed callsign
return true;
}
}
return false;
}