You've already forked fm-dx-webserver
mirror of
https://github.com/KubaPro010/fm-dx-webserver.git
synced 2026-02-26 22:13:53 +01:00
185 lines
6.9 KiB
JavaScript
185 lines
6.9 KiB
JavaScript
const { rdsEccLookup, iso, countries } = require("./rds_country.js")
|
|
|
|
class RDSDecoder {
|
|
constructor(data) {
|
|
this.data = data;
|
|
this.clear()
|
|
}
|
|
|
|
clear() {
|
|
this.data.pi = '?';
|
|
this.ps = Array(8).fill(' ');
|
|
this.ps_errors = Array(8).fill("0");
|
|
this.rt0 = Array(64).fill(' ');
|
|
this.rt0_errors = Array(64).fill("0");
|
|
this.rt1 = Array(64).fill(' ');
|
|
this.rt1_errors = Array(64).fill("0");
|
|
this.data.ps = '';
|
|
this.data.rt1 = '';
|
|
this.data.rt0 = '';
|
|
this.data.pty = 0;
|
|
this.data.tp = 0;
|
|
this.data.ta = 0;
|
|
this.data.ms = -1;
|
|
this.data.rt_flag = 0;
|
|
this.rt1_to_clear = false;
|
|
this.rt0_to_clear = false;
|
|
this.data.ecc = null;
|
|
this.data.country_name = ""
|
|
this.data.country_iso = "UN"
|
|
|
|
this.af_len = 0;
|
|
this.data.af = []
|
|
this.af_am_follows = false;
|
|
|
|
this.last_pi_error = 0;
|
|
}
|
|
|
|
decodeGroup(blockA, blockB, blockC, blockD, error) {
|
|
const a_error = (error >> 6) & 3;
|
|
const b_error = (error >> 4) & 3;
|
|
const c_error = (error >> 2) & 3;
|
|
const d_error = error & 3;
|
|
|
|
if(this.last_pi_error > a_error) {
|
|
this.data.pi = blockA.toString(16).toUpperCase().padStart(4, '0');
|
|
this.last_pi_error = a_error;
|
|
}
|
|
|
|
if(b_error !== 0) return; // B chooses what group this is, if this has errors, we are screwed
|
|
|
|
const group = (blockB >> 12) & 0xF;
|
|
const version = (blockB >> 11) & 0x1;
|
|
this.data.tp = Number((blockB >> 10) & 1);
|
|
this.data.pty = (blockB >> 5) & 31;
|
|
|
|
if (group === 0) {
|
|
this.data.ta = (blockB >> 4) & 1;
|
|
this.data.ms = (blockB >> 3) & 1;
|
|
|
|
if(version === 0 && c_error !== 3) {
|
|
var af_high = blockC >> 8;
|
|
var af_low = blockC & 0xFF;
|
|
var BASE = 224;
|
|
var FILLER = 205;
|
|
var AM_FOLLOWS = 250;
|
|
|
|
if(af_high >= BASE && af_high <= (BASE+25)) {
|
|
this.af_len = af_high-BASE;
|
|
if(this.af_len !== this.data.af.length) {
|
|
this.data.af = [];
|
|
this.af_am_follows = false;
|
|
|
|
if(af_low != FILLER && af_low != AM_FOLLOWS) this.data.af.push((af_low+875)*100)
|
|
else if(af_low == AM_FOLLOWS) this.af_am_follows = true;
|
|
}
|
|
} else if(this.data.af.length != this.af_len) {
|
|
if(!(af_high == AM_FOLLOWS || this.af_am_follows)) {
|
|
var freq = (af_high+875)*100;
|
|
if(!this.data.af.includes(freq)) this.data.af.push(freq);
|
|
}
|
|
if(this.af_am_follows) this.af_am_follows = false;
|
|
if(!(af_high == AM_FOLLOWS || af_low == FILLER || af_low == AM_FOLLOWS)) {
|
|
var freq = (af_low+875)*100;
|
|
if(!this.data.af.includes(freq)) this.data.af.push(freq);
|
|
}
|
|
if(af_low == AM_FOLLOWS) this.af_am_follows = true;
|
|
}
|
|
}
|
|
|
|
if(d_error > 2) return; // Don't risk it
|
|
|
|
const idx = blockB & 0x3;
|
|
|
|
this.ps[idx * 2] = String.fromCharCode(blockD >> 8);
|
|
this.ps[idx * 2 + 1] = String.fromCharCode(blockD & 0xFF);
|
|
this.ps_errors[idx * 2] = Math.ceil(d_error * (10/3));
|
|
this.ps_errors[idx * 2 + 1] = Math.ceil(d_error * (10/3));
|
|
|
|
this.data.ps = this.ps.join('');
|
|
this.data.ps_errors = this.ps_errors.join(',');
|
|
} else if (group === 1 && version === 0) {
|
|
if(c_error > 2) return;
|
|
var variant_code = (blockC >> 12) & 0x7;
|
|
switch (variant_code) {
|
|
case 0:
|
|
this.data.ecc = blockC & 0xff;
|
|
this.data.country_name = rdsEccLookup(blockA, this.data.ecc);
|
|
if(this.data.country_name.length === 0) this.data.country_iso = "UN";
|
|
else this.data.country_iso = iso[countries.indexOf(this.data.country_name)]
|
|
break;
|
|
default: break;
|
|
}
|
|
} else if (group === 2) {
|
|
const idx = blockB & 0b1111;
|
|
this.rt_ab = Boolean((blockB >> 4) & 1);
|
|
var multiplier = (version == 0) ? 4 : 2;
|
|
if(this.rt_ab) {
|
|
if(this.rt1_to_clear) {
|
|
this.rt1 = Array(64).fill(' ');
|
|
this.rt1_errors = Array(64).fill("0");
|
|
this.rt1_to_clear = false;
|
|
}
|
|
|
|
if(c_error < 2 && multiplier !== 2) {
|
|
this.rt1[idx * multiplier] = String.fromCharCode(blockC >> 8);
|
|
this.rt1[idx * multiplier + 1] = String.fromCharCode(blockC & 0xFF);
|
|
this.rt1_errors[idx * multiplier] = Math.ceil(c_error * (10/3));
|
|
this.rt1_errors[idx * multiplier + 1] = Math.ceil(c_error * (10/3));
|
|
}
|
|
if(d_error < 2) {
|
|
var offset = (multiplier == 2) ? 0 : 2;
|
|
this.rt1[idx * multiplier + offset] = String.fromCharCode(blockD >> 8);
|
|
this.rt1[idx * multiplier + offset + 1] = String.fromCharCode(blockD & 0xFF);
|
|
this.rt1_errors[idx * multiplier + offset] = Math.ceil(d_error * (10/3));
|
|
this.rt1_errors[idx * multiplier + offset + 1] = Math.ceil(d_error * (10/3));
|
|
}
|
|
|
|
var i = this.rt1.indexOf("\r")
|
|
while(i != -1) {
|
|
this.rt1[i] = " ";
|
|
i = this.rt1.indexOf("\r");
|
|
}
|
|
|
|
this.data.rt1 = this.rt1.join('');
|
|
this.data.rt1_errors = this.rt1_errors.join(',');
|
|
this.data.rt_flag = 1;
|
|
this.rt0_to_clear = true;
|
|
} else {
|
|
if(this.rt0_to_clear) {
|
|
this.rt0 = Array(64).fill(' ');
|
|
this.rt0_errors = Array(64).fill("0");
|
|
this.rt0_to_clear = false;
|
|
}
|
|
|
|
if(c_error !== 3 && multiplier !== 2) {
|
|
this.rt0[idx * multiplier] = String.fromCharCode(blockC >> 8);
|
|
this.rt0[idx * multiplier + 1] = String.fromCharCode(blockC & 0xFF);
|
|
this.rt0_errors[idx * multiplier] = Math.ceil(c_error * (10/3));
|
|
this.rt0_errors[idx * multiplier + 1] = Math.ceil(c_error * (10/3));
|
|
}
|
|
if(d_error !== 3) {
|
|
var offset = (multiplier == 2) ? 0 : 2;
|
|
this.rt0[idx * multiplier + offset] = String.fromCharCode(blockD >> 8);
|
|
this.rt0[idx * multiplier + offset + 1] = String.fromCharCode(blockD & 0xFF);
|
|
this.rt0_errors[idx * multiplier + offset] = Math.ceil(d_error * (10/3));
|
|
this.rt0_errors[idx * multiplier + offset + 1] = Math.ceil(d_error * (10/3));
|
|
}
|
|
|
|
var i = this.rt0.indexOf("\r");
|
|
while(i != -1) {
|
|
this.rt0[i] = " ";
|
|
i = this.rt0.indexOf("\r");
|
|
}
|
|
|
|
this.data.rt0 = this.rt0.join('');
|
|
this.data.rt0_errors = this.rt0_errors.join(',');
|
|
this.data.rt_flag = 0;
|
|
this.rt1_to_clear = true;
|
|
}
|
|
} else {
|
|
// console.log(group, version)
|
|
}
|
|
}
|
|
}
|
|
module.exports = RDSDecoder; |