UNPKG

@springfield/radio-module-baofeng

Version:

Radio module for Baofeng UV-5R series ham radios

74 lines (73 loc) 3.58 kB
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 }; } }