icom-wlan-node 0.2.5 → 0.2.6

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/README.md CHANGED
@@ -262,7 +262,7 @@ The library exposes common CI‑V operations as friendly methods. Addresses are
262
262
  - `readSquelchStatus(options?: QueryOptions) => Promise<{ raw: number; isOpen: boolean } | null>` — Squelch gate state (CI-V 0x15/0x01)
263
263
  - `readAudioSquelch(options?: QueryOptions) => Promise<{ raw: number; isOpen: boolean } | null>` — Audio squelch state (CI-V 0x15/0x05)
264
264
  - `readOvfStatus(options?: QueryOptions) => Promise<{ raw: number; isOverload: boolean } | null>` — ADC overload detection (CI-V 0x15/0x07)
265
- - `getLevelMeter(options?: QueryOptions) => Promise<{ raw: number; percent: number } | null>` — S-meter level (CI-V 0x15/0x02)
265
+ - `getLevelMeter(options?: QueryOptions) => Promise<{ raw: number; percent: number; sUnits: number; dbAboveS9?: number; dBm: number; formatted: string } | null>` — S-meter (signal strength) with physical units (CI-V 0x15/0x02)
266
266
 
267
267
  **Transmission Meters** (require PTT on):
268
268
  - `readSWR(options?: QueryOptions) => Promise<{ raw: number; swr: number; alert: boolean } | null>` — SWR meter (CI-V 0x15/0x12)
@@ -330,7 +330,8 @@ if (ovf) {
330
330
 
331
331
  const sMeter = await rig.getLevelMeter({ timeout: 2000 });
332
332
  if (sMeter) {
333
- console.log(`S-Meter: ${sMeter.percent.toFixed(1)}%`);
333
+ console.log(`S-Meter: ${sMeter.formatted} (${sMeter.sUnits.toFixed(1)} S-units, ${sMeter.dBm.toFixed(1)} dBm)`);
334
+ // Example output: "S-Meter: S9+10dB (9.9 S-units, -63.1 dBm)"
334
335
  }
335
336
 
336
337
  // Read power supply monitoring
@@ -135,6 +135,28 @@ export declare const METER_CALIBRATION: {
135
135
  readonly amps: 4;
136
136
  };
137
137
  };
138
+ /**
139
+ * S-meter (CI-V 0x15/0x02) calibration points
140
+ * Per-model calibration for signal strength meter
141
+ */
142
+ readonly SMETER: {
143
+ /**
144
+ * IC-705 S-meter calibration (from official CI-V reference manual)
145
+ * - raw=0 → S0
146
+ * - raw=120 → S9
147
+ * - raw=241 → S9+60dB
148
+ */
149
+ readonly 'IC-705': {
150
+ /** S0 reference point */
151
+ readonly S0: 0;
152
+ /** S9 reference point */
153
+ readonly S9: 120;
154
+ /** S9+60dB reference point */
155
+ readonly S9_PLUS_60DB: 241;
156
+ /** HF standard: S9 ≈ -73dBm (used for dBm estimation) */
157
+ readonly HF_S9_DBM: -73;
158
+ };
159
+ };
138
160
  };
139
161
  /**
140
162
  * Convert raw power level to percentage
@@ -147,6 +147,29 @@ exports.METER_CALIBRATION = {
147
147
  LOW: { raw: 121, amps: 2.0 },
148
148
  /** High current reference: 4A (raw=241) */
149
149
  HIGH: { raw: 241, amps: 4.0 }
150
+ },
151
+ /**
152
+ * S-meter (CI-V 0x15/0x02) calibration points
153
+ * Per-model calibration for signal strength meter
154
+ */
155
+ SMETER: {
156
+ /**
157
+ * IC-705 S-meter calibration (from official CI-V reference manual)
158
+ * - raw=0 → S0
159
+ * - raw=120 → S9
160
+ * - raw=241 → S9+60dB
161
+ */
162
+ 'IC-705': {
163
+ /** S0 reference point */
164
+ S0: 0,
165
+ /** S9 reference point */
166
+ S9: 120,
167
+ /** S9+60dB reference point */
168
+ S9_PLUS_60DB: 241,
169
+ /** HF standard: S9 ≈ -73dBm (used for dBm estimation) */
170
+ HF_S9_DBM: -73
171
+ }
172
+ // Future: Add other ICOM models here
150
173
  }
151
174
  };
152
175
  /**
@@ -160,8 +160,21 @@ export declare class IcomControl {
160
160
  */
161
161
  getConnectorWLanLevel(options?: QueryOptions): Promise<WlanLevelReading | null>;
162
162
  /**
163
- * Read generic level meter (CI-V 0x15/0x02), raw 0-255.
164
- * Many rigs return two bytes and the low byte is the level.
163
+ * Read S-meter (signal strength) level (CI-V 0x15/0x02)
164
+ * Returns complete reading with S-units, dB, and dBm conversion
165
+ *
166
+ * @param options - Query options (timeout)
167
+ * @returns S-meter reading with physical units, or null if timeout
168
+ *
169
+ * @example
170
+ * ```typescript
171
+ * const reading = await rig.getLevelMeter();
172
+ * if (reading) {
173
+ * console.log(reading.formatted); // "S9+10dB"
174
+ * console.log(reading.sUnits); // 9.99
175
+ * console.log(reading.dBm); // -63.08
176
+ * }
177
+ * ```
165
178
  */
166
179
  getLevelMeter(options?: QueryOptions): Promise<LevelMeterReading | null>;
167
180
  /**
@@ -45,6 +45,7 @@ const IcomRigCommands_1 = require("./IcomRigCommands");
45
45
  const IcomConstants_1 = require("./IcomConstants");
46
46
  const bcd_1 = require("../utils/bcd");
47
47
  const errors_1 = require("../utils/errors");
48
+ const smeter_1 = require("../utils/smeter");
48
49
  class IcomControl {
49
50
  constructor(options) {
50
51
  this.ev = new events_1.EventEmitter();
@@ -756,8 +757,21 @@ class IcomControl {
756
757
  };
757
758
  }
758
759
  /**
759
- * Read generic level meter (CI-V 0x15/0x02), raw 0-255.
760
- * Many rigs return two bytes and the low byte is the level.
760
+ * Read S-meter (signal strength) level (CI-V 0x15/0x02)
761
+ * Returns complete reading with S-units, dB, and dBm conversion
762
+ *
763
+ * @param options - Query options (timeout)
764
+ * @returns S-meter reading with physical units, or null if timeout
765
+ *
766
+ * @example
767
+ * ```typescript
768
+ * const reading = await rig.getLevelMeter();
769
+ * if (reading) {
770
+ * console.log(reading.formatted); // "S9+10dB"
771
+ * console.log(reading.sUnits); // 9.99
772
+ * console.log(reading.dBm); // -63.08
773
+ * }
774
+ * ```
761
775
  */
762
776
  async getLevelMeter(options) {
763
777
  const timeoutMs = options?.timeout ?? 3000;
@@ -771,10 +785,9 @@ class IcomControl {
771
785
  if (data.length === 0)
772
786
  return null;
773
787
  const raw = data[data.length - 1] & 0xff; // use low byte as 0-255 level
774
- return {
775
- raw,
776
- percent: (raw / 255) * 100
777
- };
788
+ // Convert raw value to S-meter reading with physical units
789
+ // Uses IC-705 calibration by default (can be extended to support other models)
790
+ return (0, smeter_1.rawToSMeter)(raw, 'IC-705');
778
791
  }
779
792
  /**
780
793
  * Set WLAN connector audio level
package/dist/types.d.ts CHANGED
@@ -153,14 +153,27 @@ export interface WlanLevelReading {
153
153
  percent: number;
154
154
  }
155
155
  /**
156
- * Generic level meter (0-255) reading
157
- * For CI-V 0x15/0x02 experimental level meter
156
+ * S-meter (signal strength) level reading
157
+ * For CI-V 0x15/0x02 command
158
+ *
159
+ * Calibration (IC-705):
160
+ * - raw=0 → S0
161
+ * - raw=120 → S9
162
+ * - raw=241 → S9+60dB
158
163
  */
159
164
  export interface LevelMeterReading {
160
- /** Raw 0-255 value */
165
+ /** Raw 0-255 BCD value */
161
166
  raw: number;
162
167
  /** Percentage (0-100%) */
163
168
  percent: number;
169
+ /** S-unit value (0-9+), supports decimal (e.g., 4.5 = S4.5) */
170
+ sUnits: number;
171
+ /** dB above S9 (only when >S9, e.g., 20 means S9+20dB) */
172
+ dbAboveS9?: number;
173
+ /** Estimated absolute power in dBm (based on HF standard S9 ≈ -73dBm) */
174
+ dBm: number;
175
+ /** Human-readable formatted string (e.g., "S4", "S9+20dB") */
176
+ formatted: string;
164
177
  }
165
178
  /**
166
179
  * Squelch status reading (CI-V 0x15/0x01)
@@ -0,0 +1,27 @@
1
+ /**
2
+ * S-meter (signal strength meter) conversion utilities
3
+ * Converts raw BCD values from CI-V 0x15/0x02 to physical S-units and dBm
4
+ */
5
+ import { LevelMeterReading } from '../types';
6
+ /**
7
+ * Convert raw S-meter BCD value to complete reading with S-units, dB, and dBm
8
+ *
9
+ * @param raw - Raw BCD value (0-255) from CI-V 0x15/0x02
10
+ * @param model - Radio model name (default: 'IC-705')
11
+ * @returns Complete LevelMeterReading with all calculated fields
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * const reading = rawToSMeter(140, 'IC-705');
16
+ * console.log(reading);
17
+ * // {
18
+ * // raw: 140,
19
+ * // percent: 54.9,
20
+ * // sUnits: 9.99,
21
+ * // dbAboveS9: 9.92,
22
+ * // dBm: -63.08,
23
+ * // formatted: "S9+10dB"
24
+ * // }
25
+ * ```
26
+ */
27
+ export declare function rawToSMeter(raw: number, model?: string): LevelMeterReading;
@@ -0,0 +1,138 @@
1
+ "use strict";
2
+ /**
3
+ * S-meter (signal strength meter) conversion utilities
4
+ * Converts raw BCD values from CI-V 0x15/0x02 to physical S-units and dBm
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.rawToSMeter = rawToSMeter;
8
+ const IcomConstants_1 = require("../rig/IcomConstants");
9
+ /**
10
+ * Get S-meter calibration constants for a specific radio model
11
+ * @param model - Radio model name (e.g., 'IC-705')
12
+ * @returns Calibration constants, defaults to IC-705 if model not found
13
+ */
14
+ function getCalibration(model = 'IC-705') {
15
+ const cal = IcomConstants_1.METER_CALIBRATION.SMETER[model];
16
+ if (!cal) {
17
+ // Default to IC-705 if model not found
18
+ console.warn(`S-meter calibration for model '${model}' not found, using IC-705 defaults`);
19
+ return IcomConstants_1.METER_CALIBRATION.SMETER['IC-705'];
20
+ }
21
+ return cal;
22
+ }
23
+ /**
24
+ * Convert raw S-meter value to S-units (0-9+)
25
+ * Uses linear interpolation based on calibration points
26
+ *
27
+ * @param raw - Raw BCD value (0-255)
28
+ * @param cal - Calibration constants
29
+ * @returns S-unit value (0-9+, supports decimals)
30
+ */
31
+ function rawToSUnits(raw, cal) {
32
+ // Clamp to valid range
33
+ if (raw <= cal.S0)
34
+ return 0;
35
+ if (raw >= cal.S9_PLUS_60DB) {
36
+ // Maximum S9+60dB = S9 + 60dB/6dB per S-unit = S9 + 10 S-units = S19
37
+ return 9 + 60 / 6;
38
+ }
39
+ // Linear interpolation
40
+ if (raw <= cal.S9) {
41
+ // S0 to S9: linear from 0 to 9
42
+ return (raw - cal.S0) * 9.0 / (cal.S9 - cal.S0);
43
+ }
44
+ else {
45
+ // Above S9: each S-unit = 6dB
46
+ const dbAboveS9 = (raw - cal.S9) * 60.0 / (cal.S9_PLUS_60DB - cal.S9);
47
+ return 9 + dbAboveS9 / 6.0;
48
+ }
49
+ }
50
+ /**
51
+ * Convert raw S-meter value to dB above S9
52
+ * Only meaningful when raw > S9 threshold
53
+ *
54
+ * @param raw - Raw BCD value (0-255)
55
+ * @param cal - Calibration constants
56
+ * @returns dB above S9, or undefined if below S9
57
+ */
58
+ function rawToDbAboveS9(raw, cal) {
59
+ if (raw <= cal.S9) {
60
+ return undefined; // Below S9, no "dB above S9" concept
61
+ }
62
+ // Linear interpolation: S9 to S9+60dB
63
+ const dbAboveS9 = (raw - cal.S9) * 60.0 / (cal.S9_PLUS_60DB - cal.S9);
64
+ return Math.max(0, dbAboveS9); // Clamp to non-negative
65
+ }
66
+ /**
67
+ * Estimate absolute power in dBm
68
+ * Based on HF standard: S9 ≈ -73dBm
69
+ * NOTE: This is an estimation and may vary by band, filter, and device settings
70
+ *
71
+ * @param sUnits - S-unit value
72
+ * @param cal - Calibration constants
73
+ * @returns Estimated power in dBm
74
+ */
75
+ function estimateDpm(sUnits, cal) {
76
+ // Each S-unit below S9 = 6dB
77
+ // Each dB above S9 = 1dB
78
+ const dbRelativeToS9 = (sUnits - 9) * 6.0;
79
+ return cal.HF_S9_DBM + dbRelativeToS9;
80
+ }
81
+ /**
82
+ * Format S-meter reading as human-readable string
83
+ * Examples: "S0", "S4", "S9", "S9+10dB", "S9+60dB"
84
+ *
85
+ * @param sUnits - S-unit value
86
+ * @param dbAboveS9 - dB above S9 (if any)
87
+ * @returns Formatted string
88
+ */
89
+ function formatSMeter(sUnits, dbAboveS9) {
90
+ if (dbAboveS9 !== undefined && dbAboveS9 > 0) {
91
+ // Above S9: show as "S9+XdB"
92
+ const roundedDb = Math.round(dbAboveS9);
93
+ return `S9+${roundedDb}dB`;
94
+ }
95
+ else {
96
+ // S0-S9: show as "SX"
97
+ const roundedS = Math.floor(sUnits);
98
+ return `S${roundedS}`;
99
+ }
100
+ }
101
+ /**
102
+ * Convert raw S-meter BCD value to complete reading with S-units, dB, and dBm
103
+ *
104
+ * @param raw - Raw BCD value (0-255) from CI-V 0x15/0x02
105
+ * @param model - Radio model name (default: 'IC-705')
106
+ * @returns Complete LevelMeterReading with all calculated fields
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const reading = rawToSMeter(140, 'IC-705');
111
+ * console.log(reading);
112
+ * // {
113
+ * // raw: 140,
114
+ * // percent: 54.9,
115
+ * // sUnits: 9.99,
116
+ * // dbAboveS9: 9.92,
117
+ * // dBm: -63.08,
118
+ * // formatted: "S9+10dB"
119
+ * // }
120
+ * ```
121
+ */
122
+ function rawToSMeter(raw, model = 'IC-705') {
123
+ const cal = getCalibration(model);
124
+ // Calculate all fields
125
+ const sUnits = rawToSUnits(raw, cal);
126
+ const dbAboveS9 = rawToDbAboveS9(raw, cal);
127
+ const dBm = estimateDpm(sUnits, cal);
128
+ const formatted = formatSMeter(sUnits, dbAboveS9);
129
+ const percent = (raw / 255) * 100;
130
+ return {
131
+ raw,
132
+ percent: Math.round(percent * 10) / 10, // Round to 1 decimal place
133
+ sUnits: Math.round(sUnits * 100) / 100, // Round to 2 decimal places
134
+ dbAboveS9: dbAboveS9 !== undefined ? Math.round(dbAboveS9 * 100) / 100 : undefined,
135
+ dBm: Math.round(dBm * 100) / 100, // Round to 2 decimal places
136
+ formatted
137
+ };
138
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "icom-wlan-node",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "Icom WLAN (CI‑V, audio) protocol implementation for Node.js/TypeScript.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",