# Award System Specification ## Overview This specification describes a JSON-driven award calculation system for amateur radio QSO (contact) tracking applications. The system calculates progress toward awards based on QSO data stored in a database. The award definitions are externalized as JSON files, allowing awards to be added, modified, or extended without changing application code. --- ## Architecture Overview ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ AWARD SYSTEM │ ├─────────────────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │ │ │ Award Definitions│ �───> │ Rule Processor │ ──> │ QSO Database │ │ │ │ (JSON files) │ │ (Engine Core) │ │ │ │ │ └──────────────────┘ └──────────────────┘ └──────────────────┘ │ │ │ │ │ │ │ │ │ │ v v │ │ ┌──────────────────┐ ┌──────────────┐ │ │ │ Award Metadata │ │ QSO Records │ │ │ │ - id, name │ │ - callsign │ │ │ │ - description │ │ - qsoDate │ │ │ │ - caption │ │ - band │ │ │ │ - category │ │ - mode │ │ │ │ - rules │ │ - entityId │ │ │ └──────────────────┘ │ - state │ │ │ │ - grid │ │ │ │ - darcDok │ │ │ │ - satName │ │ │ │ - lotw... │ │ │ │ - dclQsl... │ │ │ └──────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` --- ## Award Definition JSON Schema ### Root Object Structure Every award definition is a JSON object with the following structure: ```json { "id": "string (required, unique identifier)", "name": "string (required, display name)", "description": "string (required, short description)", "caption": "string (required, detailed explanation)", "category": "string (required, grouping category)", "rules": { "type": "string (required, rule type)", "...": "rule-specific properties" } } ``` ### Root Properties | Property | Type | Required | Description | |----------|------|----------|-------------| | `id` | string | Yes | Unique identifier for the award (e.g., "dxcc-mixed", "dld-80m") | | `name` | string | Yes | Display name shown in UI (e.g., "DXCC Mixed Mode", "DLD 80m") | | `description` | string | Yes | Short description for list views | | `caption` | string | Yes | Detailed explanation of award requirements | | `category` | string | Yes | Grouping category for UI organization (e.g., "dxcc", "darc", "vucc") | | `rules` | object | Yes | Award calculation rules (see Rule Types below) | --- ## Rule Types The system supports **5 rule types**. Each type defines a different calculation method. ### 1. Entity Rule Type (`"type": "entity"`) Counts unique entities (DXCC countries, states, grid squares, callsigns) from QSOs. #### Properties | Property | Type | Required | Description | |----------|------|----------|-------------| | `type` | string | Yes | Must be `"entity"` | | `entityType` | string | Yes | What to count: `"dxcc"`, `"state"`, `"grid"`, `"callsign"` | | `target` | number | Yes | Number required to complete award | | `displayField` | string | No | Field to display as entity name (defaults to entity value) | | `filters` | object | No | Filter criteria (see Filters section) | #### Entity Type Mappings | `entityType` Value | Source Field | Notes | |-------------------|--------------|-------| | `"dxcc"` | `entityId` | Numeric DXCC entity ID | | `"state"` | `state` | US state or other regional code | | `"grid"` | `grid` | First 4 characters only (e.g., "FN31") | | `"callsign"` | `callsign` | Station callsign | #### Calculation Logic ``` worked = count of unique entities where QSO exists confirmed = count of unique entities where lotwQslRstatus === 'Y' ``` #### Example: DXCC Mixed Mode ```json { "id": "dxcc-mixed", "name": "DXCC Mixed Mode", "description": "Confirm 100 DXCC entities on any band/mode", "caption": "Contact and confirm 100 different DXCC entities. Any band and mode combination counts.", "category": "dxcc", "rules": { "type": "entity", "entityType": "dxcc", "target": 100, "displayField": "entity" } } ``` #### Example: VUCC Satellite (with filter) ```json { "id": "vucc-satellite", "name": "VUCC Satellite", "description": "Confirm 100 unique grid squares via satellite", "caption": "Contact and confirm 100 unique 4-character grid squares via satellite.", "category": "vucc", "rules": { "type": "entity", "entityType": "grid", "target": 100, "displayField": "grid", "filters": { "operator": "AND", "filters": [ { "field": "satellite", "operator": "eq", "value": true } ] } } } ``` --- ### 2. DOK Rule Type (`"type": "dok"`) Counts unique DOK (DARC Ortsverband Kennung) combinations for German amateur radio awards. #### Properties | Property | Type | Required | Description | |----------|------|----------|-------------| | `type` | string | Yes | Must be `"dok"` | | `target` | number | Yes | Number required to complete award | | `confirmationType` | string | Yes | Must be `"dcl"` (DARC Community Logbook) | | `displayField` | string | No | Field to display (typically `"darcDok"`) | | `filters` | object | No | Filter criteria (band, mode, etc.) | #### Calculation Logic ``` For each QSO with darcDok: combination = darcDok + "/" + band + "/" + mode Track unique combinations worked = count of unique (DOK, band, mode) combinations confirmed = count of combinations where dclQslRstatus === 'Y' ``` **Key Behavior**: Each unique DOK on each unique band/mode counts separately. #### Example: DLD (Deutschland Diplom) ```json { "id": "dld", "name": "DLD", "description": "Deutschland Diplom - Confirm 100 unique DOKs on different bands/modes", "caption": "Contact and confirm stations with 100 unique DOKs on different band/mode combinations.", "category": "darc", "rules": { "type": "dok", "target": 100, "confirmationType": "dcl", "displayField": "darcDok" } } ``` #### Example: DLD 80m (with filter) ```json { "id": "dld-80m", "name": "DLD 80m", "description": "Confirm 100 unique DOKs on 80m", "caption": "Contact and confirm 100 unique DOKs on the 80m band.", "category": "darc", "rules": { "type": "dok", "target": 100, "confirmationType": "dcl", "displayField": "darcDok", "filters": { "operator": "AND", "filters": [ { "field": "band", "operator": "eq", "value": "80m" } ] } } } ``` --- ### 3. Points Rule Type (`"type": "points"`) Awards points for contacting specific stations, with three counting modes. #### Properties | Property | Type | Required | Description | |----------|------|----------|-------------| | `type` | string | Yes | Must be `"points"` | | `target` | number | Yes | Points required to complete award | | `countMode` | string | No | `"perStation"`, `"perBandMode"`, or `"perQso"` (default: `"perStation"`) | | `stations` | array | Yes | Array of station definitions | #### Station Array Format ```json "stations": [ { "callsign": "STATION1", "points": 10 }, { "callsign": "STATION2", "points": 5 } ] ``` #### Count Modes | Mode | Description | |------|-------------| | `"perStation"` | Each unique station earns points once (if confirmed) | | `"perBandMode"` | Each unique (callsign, band, mode) combination earns points | | `"perQso"` | Every confirmed QSO earns points | #### Calculation Logic ``` For each QSO with callsign matching a station: points = station.points for that callsign if countMode === "perStation": Count unique callsigns (if confirmed) else if countMode === "perBandMode": Count unique (callsign, band, mode) combinations (if confirmed) else if countMode === "perQso": Count every confirmed QSO totalPoints = sum(points for confirmed entries) ``` #### Example: Wavelog Award (perBandMode) ```json { "id": "wavelog-award", "name": "Wavelog Award", "description": "Contact special stations on multiple bands and modes to earn points", "caption": "Contact special stations to earn points. Points awarded for each unique band/mode combination.", "category": "special", "rules": { "type": "points", "target": 50, "countMode": "perBandMode", "stations": [ { "callsign": "DF2ET", "points": 10 }, { "callsign": "DJ7NT", "points": 10 }, { "callsign": "HB9HIL", "points": 10 }, { "callsign": "DB4SCW", "points": 5 } ] } } ``` --- ### 4. Filtered Rule Type (`"type": "filtered"`) Creates a filtered variant of another award by applying additional criteria. #### Properties | Property | Type | Required | Description | |----------|------|----------|-------------| | `type` | string | Yes | Must be `"filtered"` | | `baseRule` | object | Yes | The base entity rule to filter | | `filters` | object | Yes | Additional filters to apply | #### Base Rule Format ```json "baseRule": { "type": "entity", "entityType": "dxcc|state|grid|callsign", "target": number, "displayField": "string" } ``` #### Example: DXCC CW ```json { "id": "dxcc-cw", "name": "DXCC CW", "description": "Confirm 100 DXCC entities using CW mode", "caption": "Contact and confirm 100 different DXCC entities using CW mode only.", "category": "dxcc", "rules": { "type": "filtered", "baseRule": { "type": "entity", "entityType": "dxcc", "target": 100, "displayField": "entity" }, "filters": { "operator": "AND", "filters": [ { "field": "mode", "operator": "eq", "value": "CW" } ] } } } ``` **Note**: At runtime, this is internally converted to an `entity` rule with the filters applied. --- ### 5. Counter Rule Type (`"type": "counter"`) Counts QSOs or unique callsigns matching filter criteria. #### Properties | Property | Type | Required | Description | |----------|------|----------|-------------| | `type` | string | Yes | Must be `"counter"` | | `target` | number | Yes | Number required to complete award | | `countBy` | string | Yes | `"qso"` (QSOs) or `"callsign"` (unique callsigns) | | `displayField` | string | No | Field to display | | `filters` | object | No | Filter criteria | #### Example: RS-44 Satellite ```json { "id": "sat-rs44", "name": "RS-44 Satellite", "description": "Work 44 QSOs on satellite RS-44", "caption": "Make 44 unique QSOs via the RS-44 satellite. Each QSO with a different callsign counts.", "category": "custom", "rules": { "type": "counter", "target": 44, "countBy": "qso", "displayField": "callsign", "filters": { "operator": "AND", "filters": [ { "field": "satName", "operator": "eq", "value": "RS-44" } ] } } } ``` **Note**: At runtime, counter rules are internally converted to `entity` rules with `entityType: "callsign"`. --- ## Filters Filters allow awards to apply criteria based on QSO fields. ### Filter Structure ```json "filters": { "operator": "AND|OR", "filters": [ { "field": "fieldName", "operator": "operator", "value": "value" }, ... ] } ``` ### Filter Operators | Operator | Description | Example | |----------|-------------|---------| | `"eq"` | Equals | `{ "field": "band", "operator": "eq", "value": "20m" }` | | `"ne"` | Not equals | `{ "field": "mode", "operator": "ne", "value": "FM" }` | | `"in"` | In array | `{ "field": "band", "operator": "in", "value": ["20m", "40m"] }` | | `"nin"` | Not in array | `{ "field": "mode", "operator": "nin", "value": ["FM", "AM"] }` | | `"contains"` | Contains substring | `{ "field": "callsign", "operator": "contains", "value": "DL" }` | ### Available Filter Fields Any QSO database field can be filtered: | Field | Type | Example | |-------|------|---------| | `band` | string | `"80m"`, `"20m"`, `"2m"` | | `mode` | string | `"CW"`, `"SSB"`, `"FT8"` | | `callsign` | string | `"DJ7NT"` | | `entity` | string | `"Germany"` | | `entityId` | number | `230` | | `state` | string | `"CA"`, `"NY"` | | `grid` | string | `"FN31pr"` | | `satName` | string | `"AO-73"`, `"RS-44"` | | `satellite` | boolean | `true` (has satellite name) | ### Special Field: `satellite` The `satellite` field is a computed field: - Returns `true` if QSO has a non-empty `satName` - Returns `false` otherwise ### Compound Filters Multiple filters can be combined with `AND` or `OR`: ```json "filters": { "operator": "AND", "filters": [ { "field": "band", "operator": "eq", "value": "80m" }, { "field": "mode", "operator": "eq", "value": "CW" } ] } ``` This creates: DLD 80m CW (only QSOs on 80m band AND CW mode count). --- ## QSO Database Schema The award system relies on QSO records with specific fields. ### Required QSO Fields | Field | Type | Description | |-------|------|-------------| | `userId` | number | User/owner identifier | | `id` | number | Unique QSO identifier | | `callsign` | string | Station callsign contacted | | `qsoDate` | string | QSO date (YYYY-MM-DD) | | `timeOn` | string | QSO time (HH:MM) | | `band` | string | Amateur band (e.g., "20m", "80m") | | `mode` | string | Mode (e.g., "CW", "SSB", "FT8") | ### DXCC Fields | Field | Type | Description | |-------|------|-------------| | `entityId` | number | DXCC entity ID (numeric) | | `entity` | string | DXCC entity name (e.g., "Germany") | ### US State Fields | Field | Type | Description | |-------|------|-------------| | `state` | string | US state code (e.g., "CA", "NY") | ### Grid Square Fields | Field | Type | Description | |-------|------|-------------| | `grid` | string | Maidenhead grid square (e.g., "FN31pr") | ### DOK Fields (German Awards) | Field | Type | Description | |-------|------|-------------| | `darcDok` | string | DARC Ortsverband Kennung (e.g., "F03", "P30") | ### Satellite Fields | Field | Type | Description | |-------|------|-------------| | `satName` | string | Satellite name (e.g., "AO-73", "RS-44") | ### LoTW Confirmation Fields | Field | Type | Description | |-------|------|-------------| | `lotwQslRstatus` | string | LoTW QSL received status (`"Y"` = confirmed) | | `lotwQslRdate` | string | LoTW QSL received date (YYYY-MM-DD) | ### DCL Confirmation Fields | Field | Type | Description | |-------|------|-------------| | `dclQslRstatus` | string | DCL QSL received status (`"Y"` = confirmed) | | `dclQslRdate` | string | DCL QSL received date (YYYY-MM-DD) | --- ## Progress Calculation Result The award calculation returns a standardized result object: ### Base Result Structure ```typescript { worked: number, // Count of worked entities confirmed: number, // Count of confirmed entities target: number, // Award target percentage: number, // Progress percentage (0-100) workedEntities: string[], // Array of worked entity identifiers confirmedEntities: string[] // Array of confirmed entity identifiers } ``` ### Detail Result Structure (with `includeDetails: true`) ```typescript { // ... base fields ... award: { id: string, name: string, description: string, caption: string, target: number }, entities: [ { qsoId: number, // Reference QSO ID entity: string, // Entity identifier entityId: number, // DXCC entity ID (if applicable) entityName: string, // Display name worked: boolean, confirmed: boolean, qsoDate: string, band: string, mode: string, callsign: string, lotwQslRdate?: string, // Present if confirmed dclQslRdate?: string, // Present if DCL confirmed satName?: string, // For satellite awards points?: number // For points-based awards } ], total: number, // Total entities confirmed: number // Confirmed count } ``` --- ## Implementation Guide ### Core Algorithm ``` 1. Load award definition from JSON file 2. Query QSOs for user from database 3. Apply filters (if defined in rules) 4. Based on rule type: - entity: Count unique entity values - dok: Count unique (DOK, band, mode) combinations - points: Sum points by countMode - filtered: Apply filters to base entity rule - counter: Count QSOs or callsigns 5. Return progress with worked/confirmed counts ``` ### Rule Type Resolution ``` 1. If rules.type === "filtered": Convert to entity rule with baseRule + filters 2. If rules.type === "counter": Convert to entity rule with entityType = "callsign" 3. If rules.type === "dok": Use dedicated DOK calculation 4. If rules.type === "points": Use dedicated points calculation 5. If rules.type === "entity": Use standard entity calculation ``` ### Filter Application ``` function applyFilters(qsos, filters) { if filters.operator === "AND": return qsos where ALL filter conditions match else if filters.operator === "OR": return qsos where ANY filter condition matches } function matchesFilter(qso, filter) { value = qso[filter.field] switch filter.operator: case "eq": return value === filter.value case "ne": return value !== filter.value case "in": return filter.value.includes(value) case "nin": return !filter.value.includes(value) case "contains": return value.includes(filter.value) } ``` ### Entity Value Extraction ``` function getEntityValue(qso, entityType) { switch entityType: case "dxcc": return qso.entityId case "state": return qso.state case "grid": return qso.grid.substring(0, 4) // First 4 chars case "callsign": return qso.callsign } ``` --- ## Confirmation Systems ### LoTW (Logbook of The World) - **Confirmation field**: `lotwQslRstatus === 'Y'` - **Date field**: `lotwQslRdate` - Used for: DXCC, WAS, VUCC, most entity-based awards ### DCL (DARC Community Logbook) - **Confirmation field**: `dclQslRstatus === 'Y'` - **Date field**: `dclQslRdate` - Required for: DLD award - Provides: DOK fields (`darcDok`) --- ## Award Variants Using filters, you can create award variants from a base award: | Base Award | Filter | Variant | |------------|--------|---------| | DLD | `band = "80m"` | DLD 80m | | DLD | `mode = "CW"` | DLD CW | | DLD | `band = "80m"` AND `mode = "CW"` | DLD 80m CW | | DXCC | `mode = "CW"` | DXCC CW | | DXCC | `band = "20m"` | DXCC 20m | | DXCC | `satellite = true` | DXCC Satellite | --- ## Example Awards ### 1. DXCC Mixed Mode ```json { "id": "dxcc-mixed", "name": "DXCC Mixed Mode", "description": "Confirm 100 DXCC entities on any band/mode", "caption": "Contact and confirm 100 different DXCC entities. Any band and mode combination counts.", "category": "dxcc", "rules": { "type": "entity", "entityType": "dxcc", "target": 100, "displayField": "entity" } } ``` ### 2. WAS (Worked All States) ```json { "id": "was-mixed", "name": "WAS Mixed Mode", "description": "Confirm all 50 US states", "caption": "Contact and confirm all 50 US states.", "category": "was", "rules": { "type": "entity", "entityType": "state", "target": 50, "displayField": "state", "filters": { "operator": "AND", "filters": [ { "field": "entityId", "operator": "eq", "value": 291 } ] } } } ``` ### 3. VUCC Satellite ```json { "id": "vucc-satellite", "name": "VUCC Satellite", "description": "Confirm 100 unique grid squares via satellite", "caption": "Contact and confirm 100 unique 4-character grid squares via satellite.", "category": "vucc", "rules": { "type": "entity", "entityType": "grid", "target": 100, "displayField": "grid", "filters": { "operator": "AND", "filters": [ { "field": "satellite", "operator": "eq", "value": true } ] } } } ``` ### 4. 73 on 73 (Satellite) ```json { "id": "73-on-73", "name": "73 on 73", "description": "Confirm 73 unique QSO partners on satellite AO-73", "caption": "Contact and confirm 73 different stations via the AO-73 satellite.", "category": "satellite", "rules": { "type": "entity", "entityType": "callsign", "target": 73, "displayField": "callsign", "filters": { "operator": "AND", "filters": [ { "field": "satName", "operator": "eq", "value": "AO-73" } ] } } } ``` ### 5. DLD 80m CW (Combined Filters) ```json { "id": "dld-80m-cw", "name": "DLD 80m CW", "description": "Confirm 100 unique DOKs on 80m using CW", "caption": "Contact and confirm 100 unique DOKs on 80m band using CW mode.", "category": "darc", "rules": { "type": "dok", "target": 100, "confirmationType": "dcl", "displayField": "darcDok", "filters": { "operator": "AND", "filters": [ { "field": "band", "operator": "eq", "value": "80m" }, { "field": "mode", "operator": "eq", "value": "CW" } ] } } } ``` --- ## Pseudocode Implementation ### Main Calculation Function ``` function calculateAwardProgress(userId, award, includeDetails = false): rules = normalizeRules(award.rules) // Get user's QSOs qsos = database.getQSOs(userId) // Apply filters if rules.filters: qsos = applyFilters(qsos, rules.filters) // Calculate based on rule type switch rules.type: case "entity": return calculateEntityProgress(qsos, rules, includeDetails) case "dok": return calculateDOKProgress(qsos, rules, includeDetails) case "points": return calculatePointsProgress(qsos, rules, includeDetails) ``` ### Entity Progress Calculation ``` function calculateEntityProgress(qsos, rules, includeDetails): workedEntities = new Set() confirmedEntities = new Set() entityDetails = [] for qso in qsos: entity = getEntityValue(qso, rules.entityType) if entity is null: continue workedEntities.add(entity) if qso.lotwQslRstatus === 'Y': confirmedEntities.add(entity) if includeDetails and entity not in entityDetails: entityDetails.push({ qsoId: qso.id, entity: entity, entityName: getDisplayName(qso, rules.displayField), worked: true, confirmed: true, qsoDate: qso.qsoDate, band: qso.band, mode: qso.mode, callsign: qso.callsign }) result = { worked: workedEntities.size, confirmed: confirmedEntities.size, target: rules.target, percentage: (confirmedEntities.size / rules.target) * 100 } if includeDetails: result.entities = entityDetails result.total = entityDetails.length return result ``` ### DOK Progress Calculation ``` function calculateDOKProgress(qsos, rules, includeDetails): combinations = new Map() // Key: "DOK/band/mode" for qso in qsos: dok = qso.darcDok if dok is null: continue band = qso.band || "Unknown" mode = qso.mode || "Unknown" key = `${dok}/${band}/${mode}` if not combinations.has(key): combinations.set(key, { qsoId: qso.id, entity: dok, entityName: dok, band: band, mode: mode, callsign: qso.callsign, worked: false, confirmed: false }) detail = combinations.get(key) detail.worked = true if qso.dclQslRstatus === 'Y': detail.confirmed = true detail.dclQslRdate = qso.dclQslRdate workedDOKs = new Set() confirmedDOKs = new Set() for [key, detail] in combinations: workedDOKs.add(detail.entity) if detail.confirmed: confirmedDOKs.add(detail.entity) result = { worked: workedDOKs.size, confirmed: confirmedDOKs.size, target: rules.target, percentage: (confirmedDOKs.size / rules.target) * 100 } if includeDetails: result.entities = Array.from(combinations.values()) result.total = result.entities.length return result ``` ### Points Progress Calculation ``` function calculatePointsProgress(qsos, rules, includeDetails): stationPoints = new Map() for station in rules.stations: stationPoints.set(station.callsign.toUpperCase(), station.points) workedStations = new Set() totalPoints = 0 stationDetails = [] if rules.countMode === "perBandMode": combinationMap = new Map() for qso in qsos: callsign = qso.callsign.toUpperCase() points = stationPoints.get(callsign) if not points: continue band = qso.band || "Unknown" mode = qso.mode || "Unknown" key = `${callsign}/${band}/${mode}` if not combinationMap.has(key): combinationMap.set(key, { qsoId: qso.id, callsign: callsign, band: band, mode: mode, points: points, worked: true, confirmed: false }) if qso.lotwQslRstatus === 'Y': detail = combinationMap.get(key) if not detail.confirmed: detail.confirmed = true details = Array.from(combinationMap.values()) totalPoints = details.filter(d => d.confirmed).reduce(sum, d.points) result = { worked: workedStations.size, totalPoints: totalPoints, target: rules.target, percentage: (totalPoints / rules.target) * 100 } if includeDetails: result.entities = details.map(formatEntity) result.total = details.length return result ``` --- ## Language Implementation Notes ### Data Structure Recommendations 1. **Use Sets for uniqueness**: When counting unique entities, use hash sets for O(1) lookups 2. **Use Maps for combinations**: When tracking (DOK, band, mode) combinations, use composite keys 3. **Normalize early**: Convert filtered/counter rules to entity rules at load time ### Performance Considerations 1. **Cache award definitions**: Load once at startup, not per calculation 2. **Database queries**: Apply filters at database level when possible (SQL WHERE clauses) 3. **Pagination**: For large QSO datasets, process in batches 4. **Index fields**: Ensure database indexes on userId, entityId, band, mode, callsign ### Confirmation Field Handling ```typescript // Helper function for unified confirmation checking function isConfirmed(qso): boolean { return qso.lotwQslRstatus === 'Y' || qso.dclQslRstatus === 'Y' } ``` --- ## Extension Points ### Adding New Rule Types To add a new rule type: 1. Define the rule schema (properties, required fields) 2. Implement calculation function 3. Add case to main calculation switch 4. Add example award definition ### Adding New Entity Types To add a new entity type: 1. Add to `entityType` enum in entity rule 2. Add case to `getEntityValue()` function 3. Ensure QSO database has the field 4. Add display field mapping if needed ### Adding New Filter Operators To add a new filter operator: 1. Add operator name to filter schema 2. Implement comparison logic in `matchesFilter()` 3. Add documentation with examples --- ## Summary The award system is a flexible, JSON-driven framework for calculating amateur radio award progress. Key design principles: 1. **Externalized definitions**: Awards defined as JSON, not code 2. **Rule-based calculation**: Different rule types for different award patterns 3. **Filter-based variants**: Create awards variants without new rule types 4. **Standardized output**: Consistent progress format across all award types 5. **Confirmation system aware**: Supports LoTW and DCL separately This specification provides all information needed to implement the system in any programming language.