@springfield/radio-module-baofeng
Version:
Radio module for Baofeng UV-5R series ham radios
74 lines (73 loc) • 3.58 kB
JavaScript
import { Frequency, RadioToneType } from "@springfield/ham-radio-api";
import { dcsValues } from "./baofeng-dcs-tones.js";
export class BaofengDecoder {
config;
logger;
constructor(config, logger) {
this.config = config;
this.logger = logger;
}
decode(memory) {
const radioProgram = {
channels: [],
settings: {},
};
for (let channelNumber = 0; channelNumber < this.config.numberChannels; channelNumber += 1) {
this.logger.debug(`Decoding channel: ${channelNumber}`);
const channelAddress = this.getChannelAddress(channelNumber);
if (memory.contents[channelAddress] !== 0xFF) {
const channel = this.decodeChannel(memory, channelAddress, channelNumber);
radioProgram.channels.push(channel);
}
}
return radioProgram;
}
getChannelAddress(channelNumber) {
return channelNumber * this.config.channelSize;
}
decodeChannel(memory, channelAddress, channelNumber) {
const transmitPower = this.decodePower(memory.contents[channelAddress + this.config.powerOffset]);
const name = this.decodeChannelName(channelNumber, memory);
const receiveFrequency = this.decodeFrequency(memory.contents, channelAddress, this.config.receiveFrequencyOffset);
const transmitFrequency = this.decodeFrequency(memory.contents, channelAddress, this.config.transmitFrequencyOffset);
const receiveTone = this.decodeTone(memory.contents, channelAddress, this.config.receiveToneOffset);
const transmitTone = this.decodeTone(memory.contents, channelAddress, this.config.transmitToneOffset);
const radioChannel = { name, receiveFrequency, receiveTone, transmitFrequency, transmitTone };
const radioSpecificChannelSettings = { transmitPower };
return { channelNumber, radioChannel, settings: radioSpecificChannelSettings };
}
decodePower(power) {
return power === 0x0 ? 5 : 1;
}
decodeChannelName(channelNumber, memory) {
// Channel names start at 0x1000 and each name is 16 bytes apart (0x10 offset)
const channelNameAddress = 0x1000 + (channelNumber * 0x10);
let channelName = "";
// Read 7 characters (8th byte should be 0xFF terminator)
for (let index = 0; index < 7; index += 1) {
const value = memory.contents[channelNameAddress + index];
if (value !== 0xFF && value !== 0x00) {
channelName += String.fromCodePoint(value);
}
}
return channelName.trim();
}
decodeFrequency(memoryData, channelOffset, valueOffset) {
let value = 0;
for (let index = 0; index < 4; index += 1) {
value |= memoryData[channelOffset + valueOffset + index] << (8 * index);
}
return Frequency(Number.parseInt(value.toString(16), 10) * 10);
}
decodeTone(memoryData, channelOffset, valueOffset) {
// DCS: 1 byte index, CTCSS: 2 bytes value
const dcsIndex = memoryData[channelOffset + valueOffset];
const ctcssValue = (memoryData[channelOffset + valueOffset] & 0xFF) | (memoryData[channelOffset + valueOffset + 1] << 8);
// Heuristic: if the second byte is 0, treat as DCS (index), else CTCSS
if (memoryData[channelOffset + valueOffset + 1] === 0) {
const dcs = dcsValues[dcsIndex] ?? 0;
return { tone: dcs, type: RadioToneType.DCS };
}
return { tone: ctcssValue, type: RadioToneType.CTCSS };
}
}