HamBench Architecture Reference
Looking for a high-level introduction? See the Architecture Overview in the Getting Started section.
This document provides a comprehensive technical reference for the HamBench software ecosystem architecture, including detailed analysis of module responsibilities, APIs, data flow, design patterns, security, performance, and extensibility.
System Overview
The HamBench ecosystem is designed as a modular, extensible platform for amateur radio programming and management. It consists of five core modules that work together to provide a complete solution for radio configuration, communication, and data management.
Module Analysis
1. @springfield/ham-radio-api - Core Type Definitions
Purpose: Provides the foundational type system and interfaces that define the entire ecosystem.
Key Responsibilities:
- Define core data types and interfaces for radio operations
- Provide branded types for type safety (Frequency, RadioChannelId, etc.)
- Define radio communication protocols and memory structures
- Establish the contract between all other modules
Core Components:
- Branded Types: Type-safe identifiers for frequencies, channels, models, etc.
- Radio Interfaces:
RadioDriver,RadioCodec,RadioMemory, etc. - Spectrum Management: Band, mode, and privilege definitions
- License Management: Amateur radio license class definitions
Key Exports:
// Core radio interfaces
export interface RadioDriver {
readRadio(serialPortPath: string, progressIndicator: RadioProgressIndicator): Promise<RadioMemory>;
writeRadio(serialPortPath: string, memory: RadioMemory, progressIndicator: RadioProgressIndicator): Promise<void>;
}
export interface RadioCodec {
decode(memory: RadioMemory): RadioProgram;
encode(program: RadioProgram, memory: RadioMemory): RadioMemory;
}
// Branded types for type safety
export type Frequency = Brand<number, 'Frequency'>;
export type RadioChannelId = Brand<string, 'RadioChannelId'>;
export type RadioModelId = Brand<string, 'RadioModelId'>;Dependencies: Minimal - only loglayer and ts-brand for logging and type safety.
2. @springfield/ham-radio-driver - Protocol Interpreter
Purpose: Implements the core radio communication logic using a DSL-based protocol system.
Key Responsibilities:
- Execute radio communication protocols defined in JSON
- Handle serial port communication with radios
- Manage protocol step execution and error handling
- Provide progress tracking and cancellation support
Core Components:
- RadioDriver: Main driver class for reading/writing radio memory
- ProtocolInterpreter: Executes protocol steps defined in configuration
- Step Executors: Specialized executors for different protocol operations
- Serial Communication: Handles low-level serial port operations
Key Features:
export class RadioDriver {
async readRadio(serialPortPath: string, progressIndicator: RadioProgressIndicator): Promise<Uint8Array>
async writeRadio(serialPortPath: string, data: Uint8Array, progressIndicator: RadioProgressIndicator): Promise<void>
}
export class ProtocolInterpreter {
async executeProtocol(protocol: any, operation: 'readMemory' | 'writeMemory', buffer?: Uint8Array): Promise<void>
}Step Executors:
SendExecutor: Sends data to radioReceiveExecutor: Receives data from radioReadSegmentExecutor: Reads memory segmentsWriteSegmentExecutor: Writes memory segmentsSetVariableExecutor: Sets protocol variablesSendReceiveExecutor: Combined send/receive operations
Dependencies: @springfield/ham-radio-api, @springfield/ham-radio-utils, serialport, loglayer
3. @springfield/ham-radio-utils - Shared Utilities
Purpose: Provides common utilities, data structures, and helper functions used across the ecosystem.
Key Responsibilities:
- Memory management and data conversion utilities
- Schema validation and JSON processing
- UI logging and progress reporting
- Test data factories and utilities
- Band plan and frequency management
- Operator-class mapping for FCC / Callook license lookups
Core Components:
- Memory Utilities:
SegmentedMemory, memory data conversion functions - Schema Validation: JSON schema validation using Ajv
- UI Logging: Progress reporting and command-level logging
- Data Conversion: BCD conversion, hex formatting, frequency display
- Band Plan: Band lookup and license-class privilege checks (
BandPlan.hasPrivilege) - License Mapping:
operatorClassToLicenseClassIdfor Callook / FCC operator classes - Test Utilities: Factory classes for generating test data
Key Features:
// Memory management
export class SegmentedMemory {
constructor(segments: MemorySegment[])
getSegment(index: number): Uint8Array
setSegment(index: number, data: Uint8Array): void
}
// Schema validation
export function validateSchema(data: any, schema: any): ValidationResult
// UI logging
export interface UILogger {
startCommand(stepIndex: number, totalSteps: number, operation: string, step: any): void
logCommandSuccess(stepIndex: number, totalSteps: number, operation: string, step: any, context: any): void
logCommandFailure(stepIndex: number, totalSteps: number, operation: string, step: any, error: Error, context: any): void
}Dependencies: @springfield/ham-radio-api, ajv, fishery, loglayer, sprintf-js
4. @springfield/ham-radio-registry - Module Discovery and Management
Purpose: Loads radio JSON configs for the desktop catalog and for Node tooling.
Key Responsibilities:
- Discover radio configurations from installed modules (GitHub catalog in the app;
node_modulesin Node) - Validate and load radio configurations
- Manage shared components across modules
- Handle plugin installation and updates
- Provide codec factory management
Core Components:
- RadioConfigRegistry: Main interface for configuration management
- NpmBasedConfigRegistry: Implementation using npm for module discovery
- SharedComponentManager: Manages shared schemas, protocols, and codecs
- NpmClient: Interfaces with npm registry for package information
Key Features:
export interface RadioConfigRegistry {
discoverConfigurations(): Promise<RegistryRadio[]>
getConfiguration(configId: string): Promise<RegistryRadio | null>
installPlugin(moduleId: string): Promise<void>
getCodec(modelId: RadioModelId): Promise<RadioCodec | null>
validateConfiguration(config: RegistryRadio): ValidationResult
}Plugin Discovery Process:
- Load configs from an installed JSON catalog (desktop) or scan workspace /
node_modulespackages withspringfield.pluginType: "radio-module"(Node) - Load configuration files from
configs/directory - Validate configurations against schemas
- Resolve shared components and dependencies
- Cache configurations for fast access
Dependencies: @springfield/ham-radio-api, loglayer
For detailed registry architecture information, see the Registry Architecture documentation.
5. radio-module-baofeng - Radio-Specific Implementation
Purpose: JSON radio module for Baofeng UV-5R series radios.
Key Responsibilities:
- Provide radio-specific protocol, serial, and memory JSON
- Point
codec.typeat"memoryMap"and$refa memory-map JSON - Define channel/settings schemas
Module Structure:
radio-module-baofeng/
├── configs/
│ └── baofeng-uv5r.json
├── src/shared/
│ ├── schemas/
│ │ ├── channel-schema.json
│ │ └── settings-schema.json
│ └── memory-maps/
│ └── uv5r-settings.jsonEncode/decode is createMemoryMapCodec() in @springfield/ham-radio-utils. This package ships JSON only.
Dependencies (dev, for tests): @springfield/ham-radio-api, @springfield/ham-radio-utils
Data Flow Architecture
1. Radio Memory Read Operation
2. Radio Memory Write Operation
3. Plugin Discovery and Loading
Design Patterns
1. Factory Pattern
Used extensively for creating codec instances and managing shared components:
export interface CodecFactory {
createCodec(modelId: RadioModelId, config: any, logger: ILogLayer): Promise<RadioCodec>
}2. Registry Pattern
The registry module implements the registry pattern for discovering and managing radio configurations:
export interface RadioConfigRegistry {
discoverConfigurations(): Promise<RegistryRadio[]>
getConfiguration(configId: string): Promise<RegistryRadio | null>
}3. Strategy Pattern
The protocol interpreter uses the strategy pattern with different step executors:
class StepExecutorRegistry {
registerExecutor(executor: StepExecutor): void
executeStep(step: any, context: ProtocolContext): Promise<void>
}4. Observer Pattern
Progress tracking and UI logging use observer patterns:
export interface RadioProgressIndicator {
setValue(value: number): void
isCanceled: boolean
}Security Considerations
1. Plugin Validation
- All plugins are validated against schemas before loading
- Package integrity is verified through npm
- Codec execution is sandboxed where possible
2. Type Safety
- Extensive use of branded types prevents type confusion
- Strict TypeScript configuration ensures compile-time safety
- Runtime validation of all external data
3. Error Handling
- Comprehensive error handling throughout the stack
- Graceful degradation when plugins fail to load
- Detailed logging for debugging and monitoring
Performance Considerations
1. Caching
- Registry caches configurations and codecs
- Driver caches protocol interpreters
- Memory operations are optimized for large data sets
2. Lazy Loading
- Codecs are loaded only when needed
- Shared components are resolved on demand
- Protocol steps are executed incrementally
3. Memory Management
- Segmented memory allows efficient handling of large radio memories
- Progress indicators support cancellation of long-running operations
- Proper cleanup of serial connections and resources
Extensibility
The architecture is designed for maximum extensibility:
- New Radio Models: Create new radio modules following the established pattern
- Custom Protocols: Extend the protocol interpreter with custom step executors
- Shared Components: Reuse schemas, protocols, and codecs across related models
- Plugin Ecosystem: Third-party developers can create and distribute radio modules
Conclusion
The HamBench ecosystem provides a robust, extensible platform for amateur radio programming. The modular architecture allows for easy addition of new radio models while maintaining type safety and performance. The registry system enables a rich ecosystem of third-party modules, while the driver layer provides reliable communication with physical radio hardware.