diff --git a/frontend/src/cps-anytone.test.ts b/frontend/src/cps-anytone.test.ts
index f1f46e3..8cd49cf 100644
--- a/frontend/src/cps-anytone.test.ts
+++ b/frontend/src/cps-anytone.test.ts
@@ -61,7 +61,7 @@ describe("generateAnytoneChannelsCsv", () => {
const repeaters = [makeRepeater()];
it("generates CSV with correct header", () => {
- const csv = generateAnytoneChannelsCsv(repeaters, tgs);
+ const csv = generateAnytoneChannelsCsv(repeaters, tgs, "tg-name");
const lines = csv.split("\r\n");
expect(lines[0]).toBe(ANYTONE_CH_HEADER);
});
@@ -71,13 +71,13 @@ describe("generateAnytoneChannelsCsv", () => {
{ id: 262, name: "TG262", slot: "1" },
{ id: 2628, name: "TG2628", slot: "2" },
];
- const csv = generateAnytoneChannelsCsv(repeaters, tgs2);
+ const csv = generateAnytoneChannelsCsv(repeaters, tgs2, "tg-name");
const lines = csv.split("\r\n");
expect(lines).toHaveLength(3); // header + 2 channels
});
it("has 56 columns per row (matching header)", () => {
- const csv = generateAnytoneChannelsCsv(repeaters, tgs);
+ const csv = generateAnytoneChannelsCsv(repeaters, tgs, "tg-name");
const lines = csv.split("\r\n");
const headerCols = lines[0].split(",").length;
expect(headerCols).toBe(56);
@@ -87,7 +87,7 @@ describe("generateAnytoneChannelsCsv", () => {
});
it("all values are double-quoted", () => {
- const csv = generateAnytoneChannelsCsv(repeaters, tgs);
+ const csv = generateAnytoneChannelsCsv(repeaters, tgs, "tg-name");
const lines = csv.split("\r\n");
for (let i = 0; i < lines.length; i++) {
const fields = lines[i].split(",");
@@ -98,13 +98,13 @@ describe("generateAnytoneChannelsCsv", () => {
});
it("includes correct frequencies with 5 decimal places", () => {
- const csv = generateAnytoneChannelsCsv(repeaters, tgs);
+ const csv = generateAnytoneChannelsCsv(repeaters, tgs, "tg-name");
expect(csv).toContain("439.50000");
expect(csv).toContain("431.90000");
});
it("includes color code and timeslot", () => {
- const csv = generateAnytoneChannelsCsv(repeaters, tgs);
+ const csv = generateAnytoneChannelsCsv(repeaters, tgs, "tg-name");
const lines = csv.split("\r\n");
const cols = parseCsvRow(lines[1]);
// RX Color Code at index 20, Slot at index 21
@@ -113,21 +113,21 @@ describe("generateAnytoneChannelsCsv", () => {
});
it("has empty Radio ID field", () => {
- const csv = generateAnytoneChannelsCsv(repeaters, tgs);
+ const csv = generateAnytoneChannelsCsv(repeaters, tgs, "tg-name");
const lines = csv.split("\r\n");
const cols = parseCsvRow(lines[1]);
expect(cols[12]).toBe("");
});
it("has Through Mode set to On", () => {
- const csv = generateAnytoneChannelsCsv(repeaters, tgs);
+ const csv = generateAnytoneChannelsCsv(repeaters, tgs, "tg-name");
const lines = csv.split("\r\n");
const cols = parseCsvRow(lines[1]);
expect(cols[36]).toBe("On");
});
it("has TxCC as last column set to 1", () => {
- const csv = generateAnytoneChannelsCsv(repeaters, tgs);
+ const csv = generateAnytoneChannelsCsv(repeaters, tgs, "tg-name");
const lines = csv.split("\r\n");
const cols = parseCsvRow(lines[1]);
expect(cols[55]).toBe("1");
diff --git a/frontend/src/cps-anytone.ts b/frontend/src/cps-anytone.ts
index f3689d9..91676a5 100644
--- a/frontend/src/cps-anytone.ts
+++ b/frontend/src/cps-anytone.ts
@@ -1,4 +1,5 @@
import type { Repeater, CpsTalkgroup } from "./types";
+import { formatChannelAlias } from "./format";
function q(v: string | number): string {
return '"' + v + '"';
@@ -37,7 +38,7 @@ function formatFreq5(mhz: number | string): string {
}
/** Generate an Anytone CPS channels CSV. */
-export function generateAnytoneChannelsCsv(repeaters: Repeater[], talkgroups: CpsTalkgroup[]): string {
+export function generateAnytoneChannelsCsv(repeaters: Repeater[], talkgroups: CpsTalkgroup[], aliasFormat: string): string {
const rows = [ANYTONE_CH_HEADER];
let num = 0;
repeaters.forEach(function (r) {
@@ -46,7 +47,7 @@ export function generateAnytoneChannelsCsv(repeaters: Repeater[], talkgroups: Cp
const cc = r.color_code || 1;
talkgroups.forEach(function (tg) {
num++;
- const chName = (r.callsign + " " + tg.name).substring(0, 16);
+ const chName = formatChannelAlias(r.callsign, tg.name, aliasFormat);
const contactName = (tg.name || "TG" + tg.id).trim().substring(0, 16);
rows.push([
num, chName, rxFreq, txFreq,
diff --git a/frontend/src/cps-motorola.test.ts b/frontend/src/cps-motorola.test.ts
index 374abc3..f14e730 100644
--- a/frontend/src/cps-motorola.test.ts
+++ b/frontend/src/cps-motorola.test.ts
@@ -39,7 +39,7 @@ describe("generateCpsXml", () => {
it("generates a valid XML document", () => {
const repeaters = [makeRepeater()];
const tgs: CpsTalkgroup[] = [{ id: 262, name: "TG262 Deutschlan", slot: "1" }];
- const xml = generateCpsXml(repeaters, tgs);
+ const xml = generateCpsXml(repeaters, tgs, "tg-name");
expect(xml).toContain('");
expect(xml).toContain("");
@@ -52,10 +52,30 @@ describe("generateCpsXml", () => {
{ id: 262, name: "TG262", slot: "1" },
{ id: 2628, name: "TG2628", slot: "1" },
];
- const xml = generateCpsXml(repeaters, tgs);
+ const xml = generateCpsXml(repeaters, tgs, "tg-name");
const matches = xml.match(/ConventionalPersonality/g);
expect(matches).toHaveLength(4); // 2 repeaters × 2 TGs
});
+
+ it("uses tg name as alias with tg-name format", () => {
+ const repeaters = [makeRepeater()];
+ const tgs: CpsTalkgroup[] = [{ id: 262, name: "TG262 Deutschlan", slot: "1" }];
+ const xml = generateCpsXml(repeaters, tgs, "tg-name");
+ expect(xml).toContain('alias="TG262 Deutschlan"');
+ });
+
+ it("prepends callsign with call-tg-ts format", () => {
+ const repeaters = [
+ makeRepeater({ callsign: "DB0ABC", freq_tx: 439.5, freq_rx: 431.9 }),
+ makeRepeater({ callsign: "DB0XYZ", freq_tx: 438.2, freq_rx: 430.6 }),
+ ];
+ const tgs: CpsTalkgroup[] = [{ id: 262, name: "262-1", slot: "1" }];
+ const xml = generateCpsXml(repeaters, tgs, "call-tg-ts");
+ expect(xml).toContain('alias="DB0ABC 262-1"');
+ expect(xml).toContain('alias="DB0XYZ 262-1"');
+ expect(xml).toContain("