@springfield/ham-radio-utils 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from './utils/band-plan.js';
5
5
  export * from './utils/frequency-display.js';
6
6
  export * from './utils/memory-channel-utils.js';
7
7
  export * from './utils/memory-data-utils.js';
8
+ export * from './utils/operator-class-mapper.js';
8
9
  export * from './utils/schema-validator.js';
9
10
  export * from './utils/to-hex-words.js';
10
11
  export * from './utils/ui-logger.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAE7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AAEjE,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAG7C,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,sCAAsC,CAAuB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAE7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AAEjE,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAG7C,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,sCAAsC,CAAuB"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export * from './utils/band-plan.js';
5
5
  export * from './utils/frequency-display.js';
6
6
  export * from './utils/memory-channel-utils.js';
7
7
  export * from './utils/memory-data-utils.js';
8
+ export * from './utils/operator-class-mapper.js';
8
9
  export * from './utils/schema-validator.js';
9
10
  export * from './utils/to-hex-words.js';
10
11
  export * from './utils/ui-logger.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAE7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AAEjE,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAE7C,mCAAmC;AACnC,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,sCAAsC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC","sourcesContent":["export * from './memory/segmented-memory.js';\n\nexport * from './test-utils/radio-channel-factory.js';\nexport * from './test-utils/radio-programmed-channel-factory.js';\n\nexport * from './utils/band-plan.js';\nexport * from './utils/frequency-display.js';\nexport * from './utils/memory-channel-utils.js';\nexport * from './utils/memory-data-utils.js';\nexport * from './utils/schema-validator.js';\nexport * from './utils/to-hex-words.js';\nexport * from './utils/ui-logger.js';\nexport * from './utils/ui-logger-factory.js';\n\n// Export the radio protocol schema\nexport { default as radioProtocolSchema } from './schemas/radio-protocol-schema.json' with { type: 'json' };\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAE7C,cAAc,uCAAuC,CAAC;AACtD,cAAc,kDAAkD,CAAC;AAEjE,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,iCAAiC,CAAC;AAChD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,kCAAkC,CAAC;AACjD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yBAAyB,CAAC;AACxC,cAAc,sBAAsB,CAAC;AACrC,cAAc,8BAA8B,CAAC;AAE7C,mCAAmC;AACnC,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,sCAAsC,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC","sourcesContent":["export * from './memory/segmented-memory.js';\n\nexport * from './test-utils/radio-channel-factory.js';\nexport * from './test-utils/radio-programmed-channel-factory.js';\n\nexport * from './utils/band-plan.js';\nexport * from './utils/frequency-display.js';\nexport * from './utils/memory-channel-utils.js';\nexport * from './utils/memory-data-utils.js';\nexport * from './utils/operator-class-mapper.js';\nexport * from './utils/schema-validator.js';\nexport * from './utils/to-hex-words.js';\nexport * from './utils/ui-logger.js';\nexport * from './utils/ui-logger-factory.js';\n\n// Export the radio protocol schema\nexport { default as radioProtocolSchema } from './schemas/radio-protocol-schema.json' with { type: 'json' };\n"]}
@@ -19,6 +19,17 @@ export declare class BandPlan {
19
19
  * @returns The license class with the given ID, or undefined if not found
20
20
  */
21
21
  findPrivilegeById(id: string): LicenseClass | undefined;
22
+ /**
23
+ * Returns whether the given license class may transmit on the frequency.
24
+ *
25
+ * Looks up the band that contains the frequency and checks that band's
26
+ * privileges list. Frequencies outside every known band return false.
27
+ *
28
+ * @param frequencyHz - Frequency in Hz
29
+ * @param licenseClassId - Springfield license-class ID
30
+ * @returns true when the class has privilege on that frequency
31
+ */
32
+ hasPrivilege(frequencyHz: number, licenseClassId: string): boolean;
22
33
  }
23
34
  export {};
24
35
  //# sourceMappingURL=band-plan.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"band-plan.d.ts","sourceRoot":"","sources":["../../src/utils/band-plan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAI/D,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,cAAc,CAA4B;IAElD,cAGC;IAED;;;;OAIG;IACH,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAE/D;IAED;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAEtD;CACF"}
1
+ {"version":3,"file":"band-plan.d.ts","sourceRoot":"","sources":["../../src/utils/band-plan.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAI/D,UAAU,YAAY;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,cAAc,CAA4B;IAElD,cAGC;IAED;;;;OAIG;IACH,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAE/D;IAED;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAEtD;IAED;;;;;;;;;OASG;IACH,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAQjE;CACF"}
@@ -23,5 +23,22 @@ export class BandPlan {
23
23
  findPrivilegeById(id) {
24
24
  return this.licenseClasses.get(id);
25
25
  }
26
+ /**
27
+ * Returns whether the given license class may transmit on the frequency.
28
+ *
29
+ * Looks up the band that contains the frequency and checks that band's
30
+ * privileges list. Frequencies outside every known band return false.
31
+ *
32
+ * @param frequencyHz - Frequency in Hz
33
+ * @param licenseClassId - Springfield license-class ID
34
+ * @returns true when the class has privilege on that frequency
35
+ */
36
+ hasPrivilege(frequencyHz, licenseClassId) {
37
+ const band = this.findBandByFrequency(frequencyHz);
38
+ if (!band) {
39
+ return false;
40
+ }
41
+ return band.privileges.includes(licenseClassId);
42
+ }
26
43
  }
27
44
  //# sourceMappingURL=band-plan.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"band-plan.js","sourceRoot":"","sources":["../../src/utils/band-plan.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,MAAM,kBAAkB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC/D,OAAO,kBAAkB,MAAM,4BAA4B,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAOlF,MAAM,OAAO,QAAQ;IACX,KAAK,CAAiB;IACtB,cAAc,CAA4B;IAElD;QACE,IAAI,CAAC,KAAK,GAAG,SAA2B,CAAC;QACzC,IAAI,CAAC,cAAc,GAAG,IAAI,GAAG,CAAE,kBAAqC,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAC/H,CAAC;IAED;;;;OAIG;IACH,mBAAmB,CAAC,SAAiB;QACnC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,IAAI,IAAI,CAAC,cAAc,IAAI,SAAS,IAAI,IAAI,CAAC,cAAc,CAAC,CAAC;IACzG,CAAC;IAED;;;;OAIG;IACH,iBAAiB,CAAC,EAAU;QAC1B,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrC,CAAC;CACF","sourcesContent":["import type { SpectrumBand } from '@springfield/ham-radio-api';\nimport bandsData from '../db/bands.json' with { type: 'json' };\nimport licenseClassesData from '../db/license-classes.json' with { type: 'json' };\n\ninterface LicenseClass {\n id: string;\n name: string;\n}\n\nexport class BandPlan {\n private bands: SpectrumBand[];\n private licenseClasses: Map<string, LicenseClass>;\n\n constructor() {\n this.bands = bandsData as SpectrumBand[];\n this.licenseClasses = new Map((licenseClassesData as LicenseClass[]).map((licenseClass) => [licenseClass.id, licenseClass]));\n }\n\n /**\n * Finds the spectrum band that contains the given frequency\n * @param frequency The frequency in Hz to find the band for\n * @returns The spectrum band containing the frequency, or undefined if no band contains it\n */\n findBandByFrequency(frequency: number): SpectrumBand | undefined {\n return this.bands.find((band) => frequency >= band.lowerFrequency && frequency <= band.upperFrequency);\n }\n\n /**\n * Finds a license class by its ID\n * @param id The ID of the license class to find\n * @returns The license class with the given ID, or undefined if not found\n */\n findPrivilegeById(id: string): LicenseClass | undefined {\n return this.licenseClasses.get(id);\n }\n}\n"]}
1
+ {"version":3,"file":"band-plan.js","sourceRoot":"","sources":["../../src/utils/band-plan.ts"],"names":[],"mappings":"AACA,OAAO,SAAS,MAAM,kBAAkB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAC/D,OAAO,kBAAkB,MAAM,4BAA4B,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAOlF,MAAM,OAAO,QAAQ;IACX,KAAK,CAAiB;IACtB,cAAc,CAA4B;IAElD;QACE,IAAI,CAAC,KAAK,GAAG,SAA2B,CAAC;QACzC,IAAI,CAAC,cAAc,GAAG,IAAI,GAAG,CAAE,kBAAqC,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC;IAC/H,CAAC;IAED;;;;OAIG;IACH,mBAAmB,CAAC,SAAiB;QACnC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,IAAI,IAAI,CAAC,cAAc,IAAI,SAAS,IAAI,IAAI,CAAC,cAAc,CAAC,CAAC;IACzG,CAAC;IAED;;;;OAIG;IACH,iBAAiB,CAAC,EAAU;QAC1B,OAAO,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACrC,CAAC;IAED;;;;;;;;;OASG;IACH,YAAY,CAAC,WAAmB,EAAE,cAAsB;QACtD,MAAM,IAAI,GAAG,IAAI,CAAC,mBAAmB,CAAC,WAAW,CAAC,CAAC;QAEnD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,KAAK,CAAC;QACf,CAAC;QAED,OAAQ,IAAI,CAAC,UAAuB,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;IAChE,CAAC;CACF","sourcesContent":["import type { SpectrumBand } from '@springfield/ham-radio-api';\nimport bandsData from '../db/bands.json' with { type: 'json' };\nimport licenseClassesData from '../db/license-classes.json' with { type: 'json' };\n\ninterface LicenseClass {\n id: string;\n name: string;\n}\n\nexport class BandPlan {\n private bands: SpectrumBand[];\n private licenseClasses: Map<string, LicenseClass>;\n\n constructor() {\n this.bands = bandsData as SpectrumBand[];\n this.licenseClasses = new Map((licenseClassesData as LicenseClass[]).map((licenseClass) => [licenseClass.id, licenseClass]));\n }\n\n /**\n * Finds the spectrum band that contains the given frequency\n * @param frequency The frequency in Hz to find the band for\n * @returns The spectrum band containing the frequency, or undefined if no band contains it\n */\n findBandByFrequency(frequency: number): SpectrumBand | undefined {\n return this.bands.find((band) => frequency >= band.lowerFrequency && frequency <= band.upperFrequency);\n }\n\n /**\n * Finds a license class by its ID\n * @param id The ID of the license class to find\n * @returns The license class with the given ID, or undefined if not found\n */\n findPrivilegeById(id: string): LicenseClass | undefined {\n return this.licenseClasses.get(id);\n }\n\n /**\n * Returns whether the given license class may transmit on the frequency.\n *\n * Looks up the band that contains the frequency and checks that band's\n * privileges list. Frequencies outside every known band return false.\n *\n * @param frequencyHz - Frequency in Hz\n * @param licenseClassId - Springfield license-class ID\n * @returns true when the class has privilege on that frequency\n */\n hasPrivilege(frequencyHz: number, licenseClassId: string): boolean {\n const band = this.findBandByFrequency(frequencyHz);\n\n if (!band) {\n return false;\n }\n\n return (band.privileges as string[]).includes(licenseClassId);\n }\n}\n"]}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Maps a Callook.info / FCC operator-class name to a Springfield license-class ID.
3
+ *
4
+ * Callook returns values such as TECHNICIAN, GENERAL, and EXTRA. Club, military,
5
+ * and RACES records often have an empty operator class and map to undefined.
6
+ *
7
+ * @param operClass - Operator class string from Callook or an equivalent FCC source
8
+ * @returns The matching license-class ID, or undefined when no personal class applies
9
+ */
10
+ export declare function operatorClassToLicenseClassId(operClass: string): string | undefined;
11
+ //# sourceMappingURL=operator-class-mapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operator-class-mapper.d.ts","sourceRoot":"","sources":["../../src/utils/operator-class-mapper.ts"],"names":[],"mappings":"AAeA;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAuBnF"}
@@ -0,0 +1,39 @@
1
+ import licenseClassesData from '../db/license-classes.json' with { type: 'json' };
2
+ const LICENSE_CLASSES = licenseClassesData;
3
+ const TECHNICIAN_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'Technician').id;
4
+ const GENERAL_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'General').id;
5
+ const EXTRA_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'Amateur Extra').id;
6
+ const ADVANCED_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'Advanced (Grandfathered)').id;
7
+ const NOVICE_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'Novice (Grandfathered)').id;
8
+ /**
9
+ * Maps a Callook.info / FCC operator-class name to a Springfield license-class ID.
10
+ *
11
+ * Callook returns values such as TECHNICIAN, GENERAL, and EXTRA. Club, military,
12
+ * and RACES records often have an empty operator class and map to undefined.
13
+ *
14
+ * @param operClass - Operator class string from Callook or an equivalent FCC source
15
+ * @returns The matching license-class ID, or undefined when no personal class applies
16
+ */
17
+ export function operatorClassToLicenseClassId(operClass) {
18
+ const normalized = operClass.trim().toUpperCase().replace(/\s+/g, ' ');
19
+ if (normalized.length === 0) {
20
+ return undefined;
21
+ }
22
+ switch (normalized) {
23
+ case 'TECHNICIAN':
24
+ case 'TECHNICIAN PLUS':
25
+ return TECHNICIAN_ID;
26
+ case 'GENERAL':
27
+ return GENERAL_ID;
28
+ case 'EXTRA':
29
+ case 'AMATEUR EXTRA':
30
+ return EXTRA_ID;
31
+ case 'ADVANCED':
32
+ return ADVANCED_ID;
33
+ case 'NOVICE':
34
+ return NOVICE_ID;
35
+ default:
36
+ return undefined;
37
+ }
38
+ }
39
+ //# sourceMappingURL=operator-class-mapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operator-class-mapper.js","sourceRoot":"","sources":["../../src/utils/operator-class-mapper.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,MAAM,4BAA4B,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAOlF,MAAM,eAAe,GAAG,kBAAoC,CAAC;AAE7D,MAAM,aAAa,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,KAAK,YAAY,CAAE,CAAC,EAAE,CAAC;AACrG,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,KAAK,SAAS,CAAE,CAAC,EAAE,CAAC;AAC/F,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,KAAK,eAAe,CAAE,CAAC,EAAE,CAAC;AACnG,MAAM,WAAW,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,KAAK,0BAA0B,CAAE,CAAC,EAAE,CAAC;AACjH,MAAM,SAAS,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,KAAK,wBAAwB,CAAE,CAAC,EAAE,CAAC;AAE7G;;;;;;;;GAQG;AACH,MAAM,UAAU,6BAA6B,CAAC,SAAiB;IAC7D,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAEvE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,YAAY,CAAC;QAClB,KAAK,iBAAiB;YACpB,OAAO,aAAa,CAAC;QACvB,KAAK,SAAS;YACZ,OAAO,UAAU,CAAC;QACpB,KAAK,OAAO,CAAC;QACb,KAAK,eAAe;YAClB,OAAO,QAAQ,CAAC;QAClB,KAAK,UAAU;YACb,OAAO,WAAW,CAAC;QACrB,KAAK,QAAQ;YACX,OAAO,SAAS,CAAC;QACnB;YACE,OAAO,SAAS,CAAC;IACrB,CAAC;AACH,CAAC","sourcesContent":["import licenseClassesData from '../db/license-classes.json' with { type: 'json' };\n\ninterface LicenseClass {\n id: string;\n name: string;\n}\n\nconst LICENSE_CLASSES = licenseClassesData as LicenseClass[];\n\nconst TECHNICIAN_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'Technician')!.id;\nconst GENERAL_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'General')!.id;\nconst EXTRA_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'Amateur Extra')!.id;\nconst ADVANCED_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'Advanced (Grandfathered)')!.id;\nconst NOVICE_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'Novice (Grandfathered)')!.id;\n\n/**\n * Maps a Callook.info / FCC operator-class name to a Springfield license-class ID.\n *\n * Callook returns values such as TECHNICIAN, GENERAL, and EXTRA. Club, military,\n * and RACES records often have an empty operator class and map to undefined.\n *\n * @param operClass - Operator class string from Callook or an equivalent FCC source\n * @returns The matching license-class ID, or undefined when no personal class applies\n */\nexport function operatorClassToLicenseClassId(operClass: string): string | undefined {\n const normalized = operClass.trim().toUpperCase().replace(/\\s+/g, ' ');\n\n if (normalized.length === 0) {\n return undefined;\n }\n\n switch (normalized) {\n case 'TECHNICIAN':\n case 'TECHNICIAN PLUS':\n return TECHNICIAN_ID;\n case 'GENERAL':\n return GENERAL_ID;\n case 'EXTRA':\n case 'AMATEUR EXTRA':\n return EXTRA_ID;\n case 'ADVANCED':\n return ADVANCED_ID;\n case 'NOVICE':\n return NOVICE_ID;\n default:\n return undefined;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@springfield/ham-radio-utils",
3
- "version": "4.0.0",
3
+ "version": "4.1.0",
4
4
  "author": "Bryan Hunt",
5
5
  "license": "MIT",
6
6
  "readme": "README.md",
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export * from './utils/band-plan.js';
7
7
  export * from './utils/frequency-display.js';
8
8
  export * from './utils/memory-channel-utils.js';
9
9
  export * from './utils/memory-data-utils.js';
10
+ export * from './utils/operator-class-mapper.js';
10
11
  export * from './utils/schema-validator.js';
11
12
  export * from './utils/to-hex-words.js';
12
13
  export * from './utils/ui-logger.js';
@@ -33,4 +33,24 @@ export class BandPlan {
33
33
  findPrivilegeById(id: string): LicenseClass | undefined {
34
34
  return this.licenseClasses.get(id);
35
35
  }
36
+
37
+ /**
38
+ * Returns whether the given license class may transmit on the frequency.
39
+ *
40
+ * Looks up the band that contains the frequency and checks that band's
41
+ * privileges list. Frequencies outside every known band return false.
42
+ *
43
+ * @param frequencyHz - Frequency in Hz
44
+ * @param licenseClassId - Springfield license-class ID
45
+ * @returns true when the class has privilege on that frequency
46
+ */
47
+ hasPrivilege(frequencyHz: number, licenseClassId: string): boolean {
48
+ const band = this.findBandByFrequency(frequencyHz);
49
+
50
+ if (!band) {
51
+ return false;
52
+ }
53
+
54
+ return (band.privileges as string[]).includes(licenseClassId);
55
+ }
36
56
  }
@@ -0,0 +1,48 @@
1
+ import licenseClassesData from '../db/license-classes.json' with { type: 'json' };
2
+
3
+ interface LicenseClass {
4
+ id: string;
5
+ name: string;
6
+ }
7
+
8
+ const LICENSE_CLASSES = licenseClassesData as LicenseClass[];
9
+
10
+ const TECHNICIAN_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'Technician')!.id;
11
+ const GENERAL_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'General')!.id;
12
+ const EXTRA_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'Amateur Extra')!.id;
13
+ const ADVANCED_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'Advanced (Grandfathered)')!.id;
14
+ const NOVICE_ID = LICENSE_CLASSES.find((licenseClass) => licenseClass.name === 'Novice (Grandfathered)')!.id;
15
+
16
+ /**
17
+ * Maps a Callook.info / FCC operator-class name to a Springfield license-class ID.
18
+ *
19
+ * Callook returns values such as TECHNICIAN, GENERAL, and EXTRA. Club, military,
20
+ * and RACES records often have an empty operator class and map to undefined.
21
+ *
22
+ * @param operClass - Operator class string from Callook or an equivalent FCC source
23
+ * @returns The matching license-class ID, or undefined when no personal class applies
24
+ */
25
+ export function operatorClassToLicenseClassId(operClass: string): string | undefined {
26
+ const normalized = operClass.trim().toUpperCase().replace(/\s+/g, ' ');
27
+
28
+ if (normalized.length === 0) {
29
+ return undefined;
30
+ }
31
+
32
+ switch (normalized) {
33
+ case 'TECHNICIAN':
34
+ case 'TECHNICIAN PLUS':
35
+ return TECHNICIAN_ID;
36
+ case 'GENERAL':
37
+ return GENERAL_ID;
38
+ case 'EXTRA':
39
+ case 'AMATEUR EXTRA':
40
+ return EXTRA_ID;
41
+ case 'ADVANCED':
42
+ return ADVANCED_ID;
43
+ case 'NOVICE':
44
+ return NOVICE_ID;
45
+ default:
46
+ return undefined;
47
+ }
48
+ }