@springfield/ham-radio-utils 3.2.6 → 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"]}
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "title": "Radio Protocol DSL Schema",
4
- "description": "Schema for defining radio communication protocols in a declarative JSON format",
4
+ "description": "Schema for defining radio communication protocols as send/expect exchanges",
5
5
  "type": "object",
6
6
  "required": [
7
7
  "id",
@@ -169,7 +169,7 @@
169
169
  },
170
170
  "endAddress": {
171
171
  "type": "integer",
172
- "description": "Ending address (inclusive) of the memory segment",
172
+ "description": "Inclusive ending address of the memory segment",
173
173
  "minimum": 0
174
174
  }
175
175
  }
@@ -194,358 +194,192 @@
194
194
  },
195
195
  "definitions": {
196
196
  "protocolStep": {
197
- "type": "object",
198
- "description": "A single protocol step",
197
+ "description": "A single protocol step: an exchange, a chunked read, or a chunked write",
199
198
  "oneOf": [
200
199
  {
201
- "type": "object",
202
- "required": [
203
- "sendReceive"
204
- ],
205
- "properties": {
206
- "sendReceive": {
207
- "$ref": "#/definitions/sendReceiveStep"
208
- }
209
- }
200
+ "$ref": "#/definitions/readStep"
210
201
  },
211
202
  {
212
- "type": "object",
213
- "required": [
214
- "send"
215
- ],
216
- "properties": {
217
- "send": {
218
- "$ref": "#/definitions/sendStep"
219
- }
220
- }
203
+ "$ref": "#/definitions/writeStep"
221
204
  },
222
205
  {
223
- "type": "object",
224
- "required": [
225
- "receive"
226
- ],
227
- "properties": {
228
- "receive": {
229
- "$ref": "#/definitions/receiveStep"
230
- }
231
- }
206
+ "$ref": "#/definitions/exchangeStep"
207
+ }
208
+ ]
209
+ },
210
+ "byteToken": {
211
+ "description": "A send/expect token: a byte, hex string (0x50), ASCII opcode (S), or placeholder ($address)",
212
+ "oneOf": [
213
+ {
214
+ "type": "integer",
215
+ "minimum": 0,
216
+ "maximum": 255
232
217
  },
233
218
  {
234
- "type": "object",
235
- "required": [
236
- "readSegment"
237
- ],
238
- "properties": {
239
- "readSegment": {
240
- "$ref": "#/definitions/readSegmentStep"
241
- }
242
- }
219
+ "type": "string",
220
+ "minLength": 1
221
+ }
222
+ ]
223
+ },
224
+ "sendData": {
225
+ "type": "array",
226
+ "description": "Bytes and placeholders to send",
227
+ "items": {
228
+ "$ref": "#/definitions/byteToken"
229
+ }
230
+ },
231
+ "expect": {
232
+ "description": "Expected serial reply: exact byte(s), opaque length, or a framed pattern",
233
+ "oneOf": [
234
+ {
235
+ "$ref": "#/definitions/byteToken"
243
236
  },
244
237
  {
245
- "type": "object",
246
- "required": [
247
- "writeSegment"
248
- ],
249
- "properties": {
250
- "writeSegment": {
251
- "$ref": "#/definitions/writeSegmentStep"
252
- }
253
- }
238
+ "type": "array",
239
+ "items": {
240
+ "$ref": "#/definitions/byteToken"
241
+ },
242
+ "minItems": 1
254
243
  },
255
244
  {
256
245
  "type": "object",
257
246
  "required": [
258
- "setVariable"
247
+ "bytes"
259
248
  ],
249
+ "additionalProperties": false,
260
250
  "properties": {
261
- "setVariable": {
262
- "$ref": "#/definitions/setVariableStep"
251
+ "bytes": {
252
+ "type": "integer",
253
+ "description": "Accept any content of this many bytes",
254
+ "minimum": 1
263
255
  }
264
256
  }
265
257
  }
266
258
  ]
267
259
  },
268
- "sendReceiveStep": {
260
+ "exchangeStep": {
269
261
  "type": "object",
270
- "description": "Send data and receive response",
271
- "required": [
272
- "send",
273
- "receive"
262
+ "description": "Send bytes and/or wait for a reply",
263
+ "anyOf": [
264
+ {
265
+ "required": [
266
+ "send"
267
+ ]
268
+ },
269
+ {
270
+ "required": [
271
+ "expect"
272
+ ]
273
+ }
274
274
  ],
275
+ "additionalProperties": false,
275
276
  "properties": {
277
+ "description": {
278
+ "type": "string",
279
+ "description": "Human-readable description of the step"
280
+ },
276
281
  "send": {
277
282
  "$ref": "#/definitions/sendData"
278
283
  },
279
- "receive": {
280
- "$ref": "#/definitions/receiveConfig"
284
+ "expect": {
285
+ "$ref": "#/definitions/expect"
281
286
  },
282
287
  "timeout": {
283
288
  "type": "integer",
284
289
  "description": "Timeout in milliseconds",
285
290
  "minimum": 1,
286
291
  "default": 5000
287
- },
288
- "description": {
289
- "type": "string",
290
- "description": "Human-readable description of the step"
291
292
  }
292
293
  }
293
294
  },
294
- "sendStep": {
295
+ "readStep": {
295
296
  "type": "object",
296
- "description": "Send data only",
297
+ "description": "Chunked memory read",
297
298
  "required": [
298
- "data"
299
+ "read"
299
300
  ],
301
+ "additionalProperties": false,
300
302
  "properties": {
301
- "data": {
302
- "$ref": "#/definitions/sendData"
303
- },
304
303
  "description": {
305
304
  "type": "string",
306
305
  "description": "Human-readable description of the step"
307
- }
308
- }
309
- },
310
- "receiveStep": {
311
- "type": "object",
312
- "description": "Receive data only",
313
- "required": [
314
- "type",
315
- "length"
316
- ],
317
- "properties": {
318
- "type": {
319
- "type": "string",
320
- "enum": [
321
- "exact",
322
- "variable",
323
- "pattern",
324
- "any"
325
- ],
326
- "description": "Type of receive operation"
327
- },
328
- "value": {
329
- "type": "integer",
330
- "description": "Expected value for exact match",
331
- "minimum": 0,
332
- "maximum": 255
333
- },
334
- "length": {
335
- "type": "integer",
336
- "description": "Expected length in bytes",
337
- "minimum": 1
338
306
  },
339
- "pattern": {
340
- "type": "array",
341
- "description": "Pattern for structured responses",
342
- "items": {
343
- "oneOf": [
344
- {
345
- "type": "string",
346
- "description": "Literal value (1 byte)"
347
- },
348
- {
349
- "type": "number",
350
- "description": "Literal value (1 byte)"
307
+ "read": {
308
+ "type": "object",
309
+ "required": [
310
+ "segments",
311
+ "send",
312
+ "expect"
313
+ ],
314
+ "additionalProperties": false,
315
+ "properties": {
316
+ "segments": {
317
+ "type": "array",
318
+ "description": "Memory segment names to read",
319
+ "items": {
320
+ "type": "string"
351
321
  },
352
- {
353
- "type": "object",
354
- "required": [
355
- "field",
356
- "size"
357
- ],
358
- "properties": {
359
- "field": {
360
- "type": "string",
361
- "description": "Field name for extracted data"
362
- },
363
- "size": {
364
- "type": "integer",
365
- "description": "Size in bytes (0 for variable length)",
366
- "minimum": 0
367
- }
368
- }
369
- }
370
- ]
371
- }
372
- },
373
- "description": {
374
- "type": "string",
375
- "description": "Human-readable description of the step"
376
- }
377
- }
378
- },
379
- "readSegmentStep": {
380
- "type": "object",
381
- "description": "Read memory segments",
382
- "required": [
383
- "segments",
384
- "startChunk",
385
- "endChunk"
386
- ],
387
- "properties": {
388
- "segments": {
389
- "type": "array",
390
- "description": "List of segment names to read",
391
- "items": {
392
- "type": "string"
322
+ "minItems": 1
323
+ },
324
+ "send": {
325
+ "$ref": "#/definitions/sendData"
326
+ },
327
+ "expect": {
328
+ "$ref": "#/definitions/expect"
329
+ },
330
+ "ack": {
331
+ "$ref": "#/definitions/exchangeStep",
332
+ "description": "Optional exchange after each chunk"
333
+ },
334
+ "timeout": {
335
+ "type": "integer",
336
+ "description": "Timeout in milliseconds for each chunk exchange",
337
+ "minimum": 1
338
+ }
393
339
  }
394
- },
395
- "startChunk": {
396
- "$ref": "#/definitions/sendReceiveStep",
397
- "description": "Protocol for starting a chunk read"
398
- },
399
- "endChunk": {
400
- "$ref": "#/definitions/sendReceiveStep",
401
- "description": "Protocol for ending a chunk read"
402
- },
403
- "description": {
404
- "type": "string",
405
- "description": "Human-readable description of the step"
406
340
  }
407
341
  }
408
342
  },
409
- "writeSegmentStep": {
343
+ "writeStep": {
410
344
  "type": "object",
411
- "description": "Write memory segments",
345
+ "description": "Chunked memory write",
412
346
  "required": [
413
- "segments",
414
- "send",
415
- "data",
416
- "receive"
347
+ "write"
417
348
  ],
349
+ "additionalProperties": false,
418
350
  "properties": {
419
- "segments": {
420
- "type": "array",
421
- "description": "List of segment names to write",
422
- "items": {
423
- "type": "string"
424
- }
425
- },
426
- "send": {
427
- "$ref": "#/definitions/sendData",
428
- "description": "Data to send for write command"
429
- },
430
- "data": {
431
- "type": "string",
432
- "description": "Expression for segment data to write"
433
- },
434
- "receive": {
435
- "$ref": "#/definitions/receiveConfig",
436
- "description": "Expected response for write command"
437
- },
438
351
  "description": {
439
352
  "type": "string",
440
353
  "description": "Human-readable description of the step"
441
- }
442
- }
443
- },
444
- "setVariableStep": {
445
- "type": "object",
446
- "description": "Set a variable value",
447
- "required": [
448
- "name",
449
- "value"
450
- ],
451
- "properties": {
452
- "name": {
453
- "type": "string",
454
- "description": "Variable name"
455
354
  },
456
- "value": {
457
- "oneOf": [
458
- {
459
- "type": "string",
460
- "description": "Expression or literal value"
461
- },
462
- {
463
- "type": "number",
464
- "description": "Numeric value"
465
- }
466
- ],
467
- "description": "Value to assign to the variable"
468
- }
469
- }
470
- },
471
- "sendData": {
472
- "type": "array",
473
- "description": "Array of data to send",
474
- "items": {
475
- "oneOf": [
476
- {
477
- "type": "string",
478
- "description": "String literal or expression"
479
- },
480
- {
481
- "type": "number",
482
- "description": "Numeric literal or expression",
483
- "minimum": 0,
484
- "maximum": 255
485
- }
486
- ]
487
- }
488
- },
489
- "receiveConfig": {
490
- "type": "object",
491
- "description": "Configuration for receiving data",
492
- "required": [
493
- "type"
494
- ],
495
- "properties": {
496
- "type": {
497
- "type": "string",
498
- "enum": [
499
- "exact",
500
- "variable",
501
- "pattern",
502
- "any"
355
+ "write": {
356
+ "type": "object",
357
+ "required": [
358
+ "segments",
359
+ "send",
360
+ "expect"
503
361
  ],
504
- "description": "Type of receive operation"
505
- },
506
- "value": {
507
- "type": "integer",
508
- "description": "Expected value for exact match",
509
- "minimum": 0,
510
- "maximum": 255
511
- },
512
- "length": {
513
- "type": "integer",
514
- "description": "Expected length in bytes",
515
- "minimum": 1
516
- },
517
- "pattern": {
518
- "type": "array",
519
- "description": "Pattern for structured responses",
520
- "items": {
521
- "oneOf": [
522
- {
523
- "type": "string",
524
- "description": "Literal value (1 byte)"
525
- },
526
- {
527
- "type": "number",
528
- "description": "Literal value (1 byte)"
362
+ "additionalProperties": false,
363
+ "properties": {
364
+ "segments": {
365
+ "type": "array",
366
+ "description": "Memory segment names to write",
367
+ "items": {
368
+ "type": "string"
529
369
  },
530
- {
531
- "type": "object",
532
- "required": [
533
- "field",
534
- "size"
535
- ],
536
- "properties": {
537
- "field": {
538
- "type": "string",
539
- "description": "Field name for extracted data"
540
- },
541
- "size": {
542
- "type": "integer",
543
- "description": "Size in bytes (0 for variable length)",
544
- "minimum": 0
545
- }
546
- }
547
- }
548
- ]
370
+ "minItems": 1
371
+ },
372
+ "send": {
373
+ "$ref": "#/definitions/sendData"
374
+ },
375
+ "expect": {
376
+ "$ref": "#/definitions/expect"
377
+ },
378
+ "timeout": {
379
+ "type": "integer",
380
+ "description": "Timeout in milliseconds for each chunk exchange",
381
+ "minimum": 1
382
+ }
549
383
  }
550
384
  }
551
385
  }
@@ -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