@bradtech/sensor-soil 1.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.
@@ -0,0 +1,37 @@
1
+ import { BaseSensorConverter, type ConversionContext, type ConversionOutput } from '@bradtech/sensor';
2
+ /**
3
+ * Raw input reading for soil bulk electrical conductivity.
4
+ */
5
+ export interface SoilEcInput {
6
+ /** Sensor depth probe location in centimeters */
7
+ depthCm: number;
8
+ /** Bulk electrical conductivity reading in mS/cm ($dS/m$) */
9
+ bulkEcMsCm: number;
10
+ /** Soil temperature at probe depth in °C */
11
+ soilTemperature?: number;
12
+ }
13
+ /**
14
+ * Soil Bulk Electrical Conductivity & Salinity Converter.
15
+ *
16
+ * Normalizes bulk EC readings to the international agronomic standard reference temperature of 25°C ($EC_{25}$):
17
+ * $$EC_{25} = \frac{EC_T}{1 + 0.019 \cdot (T_{soil} - 25)}$$
18
+ */
19
+ export declare class SoilElectricalConductivityConverter extends BaseSensorConverter<SoilEcInput> {
20
+ /** Sensor domain family classification */
21
+ readonly sensorFamily = "soil";
22
+ /** Unique algorithmic model code */
23
+ readonly modelCode = "soil-ec-temperature-normalized";
24
+ /** Algorithm semver version */
25
+ readonly modelVersion = "1.0.0";
26
+ /** Human-readable model description */
27
+ readonly description = "Temperature-normalized soil electrical conductivity converter";
28
+ /**
29
+ * Converts raw bulk EC into normalized 25°C salinity metrics with agronomic salinity classification.
30
+ *
31
+ * @param raw - Object containing probe depth, measured EC, and soil temperature.
32
+ * @param _context - Optional environmental context.
33
+ * @returns Array containing the temperature-compensated EC metric in mS/cm.
34
+ */
35
+ convert(raw: SoilEcInput, _context?: ConversionContext): ConversionOutput[];
36
+ }
37
+ //# sourceMappingURL=SoilElectricalConductivityConverter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SoilElectricalConductivityConverter.d.ts","sourceRoot":"","sources":["../src/SoilElectricalConductivityConverter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAErG;;GAEG;AACH,MAAM,WAAW,WAAW;IACzB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAA;IACf,6DAA6D;IAC7D,UAAU,EAAE,MAAM,CAAA;IAClB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;;;;GAKG;AACH,qBAAa,mCAAoC,SAAQ,mBAAmB,CAAC,WAAW,CAAC;IACtF,0CAA0C;IAC1C,QAAQ,CAAC,YAAY,UAAS;IAC9B,oCAAoC;IACpC,QAAQ,CAAC,SAAS,oCAAmC;IACrD,+BAA+B;IAC/B,QAAQ,CAAC,YAAY,WAAU;IAC/B,uCAAuC;IACvC,QAAQ,CAAC,WAAW,mEAAkE;IAEtF;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,EAAE,WAAW,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,gBAAgB,EAAE;CAyB7E"}
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SoilElectricalConductivityConverter = void 0;
4
+ const sensor_1 = require("@bradtech/sensor");
5
+ /**
6
+ * Soil Bulk Electrical Conductivity & Salinity Converter.
7
+ *
8
+ * Normalizes bulk EC readings to the international agronomic standard reference temperature of 25°C ($EC_{25}$):
9
+ * $$EC_{25} = \frac{EC_T}{1 + 0.019 \cdot (T_{soil} - 25)}$$
10
+ */
11
+ class SoilElectricalConductivityConverter extends sensor_1.BaseSensorConverter {
12
+ constructor() {
13
+ super(...arguments);
14
+ /** Sensor domain family classification */
15
+ this.sensorFamily = 'soil';
16
+ /** Unique algorithmic model code */
17
+ this.modelCode = 'soil-ec-temperature-normalized';
18
+ /** Algorithm semver version */
19
+ this.modelVersion = '1.0.0';
20
+ /** Human-readable model description */
21
+ this.description = 'Temperature-normalized soil electrical conductivity converter';
22
+ }
23
+ /**
24
+ * Converts raw bulk EC into normalized 25°C salinity metrics with agronomic salinity classification.
25
+ *
26
+ * @param raw - Object containing probe depth, measured EC, and soil temperature.
27
+ * @param _context - Optional environmental context.
28
+ * @returns Array containing the temperature-compensated EC metric in mS/cm.
29
+ */
30
+ convert(raw, _context) {
31
+ let ec25 = raw.bulkEcMsCm;
32
+ // Temperature compensation: EC_25 = EC_T / (1 + 0.019 * (T - 25))
33
+ if (raw.soilTemperature !== undefined) {
34
+ ec25 = raw.bulkEcMsCm / (1.0 + 0.019 * (raw.soilTemperature - 25.0));
35
+ }
36
+ const clamped = this.clampWithConfidence(ec25, 0, 20, 0, 10);
37
+ const depth = raw.depthCm || 10;
38
+ return [
39
+ {
40
+ metric: `okf:soil/conductivity/ec/${depth}cm`,
41
+ value: clamped.value,
42
+ unit: 'mS/cm',
43
+ qudtUri: 'qudt:unit/MilliS-PER-M',
44
+ confidence: clamped.confidence,
45
+ metadata: {
46
+ depthCm: depth,
47
+ salinityClass: ec25 < 2.0 ? 'non_saline' : ec25 < 4.0 ? 'slightly_saline' : 'moderately_saline',
48
+ },
49
+ },
50
+ ];
51
+ }
52
+ }
53
+ exports.SoilElectricalConductivityConverter = SoilElectricalConductivityConverter;
54
+ //# sourceMappingURL=SoilElectricalConductivityConverter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SoilElectricalConductivityConverter.js","sourceRoot":"","sources":["../src/SoilElectricalConductivityConverter.ts"],"names":[],"mappings":";;;AAAA,6CAAqG;AAcrG;;;;;GAKG;AACH,MAAa,mCAAoC,SAAQ,4BAAgC;IAAzF;;QACG,0CAA0C;QACjC,iBAAY,GAAG,MAAM,CAAA;QAC9B,oCAAoC;QAC3B,cAAS,GAAG,gCAAgC,CAAA;QACrD,+BAA+B;QACtB,iBAAY,GAAG,OAAO,CAAA;QAC/B,uCAAuC;QAC9B,gBAAW,GAAG,+DAA+D,CAAA;IAkCzF,CAAC;IAhCE;;;;;;OAMG;IACH,OAAO,CAAC,GAAgB,EAAE,QAA4B;QACnD,IAAI,IAAI,GAAG,GAAG,CAAC,UAAU,CAAA;QAEzB,kEAAkE;QAClE,IAAI,GAAG,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;YACrC,IAAI,GAAG,GAAG,CAAC,UAAU,GAAG,CAAC,GAAG,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC,CAAA;QACvE,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,mBAAmB,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;QAC5D,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,IAAI,EAAE,CAAA;QAE/B,OAAO;YACJ;gBACG,MAAM,EAAE,4BAA4B,KAAK,IAAI;gBAC7C,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,IAAI,EAAE,OAAO;gBACb,OAAO,EAAE,wBAAwB;gBACjC,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,QAAQ,EAAE;oBACP,OAAO,EAAE,KAAK;oBACd,aAAa,EAAE,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,GAAG,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,mBAAmB;iBACjG;aACH;SACH,CAAA;IACJ,CAAC;CACH;AA1CD,kFA0CC"}
@@ -0,0 +1,43 @@
1
+ import { BaseSensorConverter, type ConversionContext, type ConversionOutput } from '@bradtech/sensor';
2
+ /**
3
+ * Raw input parameters for soil volumetric water content evaluation.
4
+ */
5
+ export interface SoilMoistureInput {
6
+ /** Sensor depth probe location in centimeters (10, 20, 30 cm) */
7
+ depthCm: 10 | 20 | 30 | number;
8
+ /** Raw dielectric permittivity or capacitive reading from the probe */
9
+ rawValue: number;
10
+ /** Soil temperature at the same depth in °C for thermal drift compensation */
11
+ soilTemperature?: number;
12
+ }
13
+ /**
14
+ * Multi-depth Soil Moisture & Volumetric Water Content (VWC %) Converter.
15
+ *
16
+ * Implements:
17
+ * - Texture-specific linear regression calibration curves (Sand, Loam, Clay, Silt, Peat):
18
+ * $$VWC = a \cdot \text{raw} + b$$
19
+ * - Custom plot-specific laboratory calibration model overrides ($y = \text{slope} \cdot x + \text{intercept}$).
20
+ * - Dielectric permittivity temperature compensation normalized to 20°C:
21
+ * $$VWC_{comp} = VWC - (T_{soil} - 20) \cdot 0.04$$
22
+ */
23
+ export declare class SoilMoistureConverter extends BaseSensorConverter<SoilMoistureInput> {
24
+ /** Sensor domain family classification */
25
+ readonly sensorFamily = "soil";
26
+ /** Unique algorithmic model code */
27
+ readonly modelCode = "soil-vwc-texture-calibrated";
28
+ /** Algorithm semver version */
29
+ readonly modelVersion = "1.0.0";
30
+ /** Human-readable model description */
31
+ readonly description = "Texture-calibrated multi-depth soil volumetric water content converter";
32
+ /** Standard USDA soil texture regression coefficients [slope a, intercept b] */
33
+ private static TEXTURE_COEFFICIENTS;
34
+ /**
35
+ * Converts raw capacitive probe readings into physical volumetric water content percentage (% VWC).
36
+ *
37
+ * @param raw - Raw sensor reading containing depth and uncalibrated value.
38
+ * @param context - Optional context providing soil texture classification or custom laboratory calibration models.
39
+ * @returns Array containing the calibrated soil moisture metric for the corresponding depth.
40
+ */
41
+ convert(raw: SoilMoistureInput, context?: ConversionContext): ConversionOutput[];
42
+ }
43
+ //# sourceMappingURL=SoilMoistureConverter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SoilMoistureConverter.d.ts","sourceRoot":"","sources":["../src/SoilMoistureConverter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAErG;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAC/B,iEAAiE;IACjE,OAAO,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,MAAM,CAAA;IAC9B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAA;IAChB,8EAA8E;IAC9E,eAAe,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;;;;;;;;GASG;AACH,qBAAa,qBAAsB,SAAQ,mBAAmB,CAAC,iBAAiB,CAAC;IAC9E,0CAA0C;IAC1C,QAAQ,CAAC,YAAY,UAAS;IAC9B,oCAAoC;IACpC,QAAQ,CAAC,SAAS,iCAAgC;IAClD,+BAA+B;IAC/B,QAAQ,CAAC,YAAY,WAAU;IAC/B,uCAAuC;IACvC,QAAQ,CAAC,WAAW,4EAA2E;IAE/F,gFAAgF;IAChF,OAAO,CAAC,MAAM,CAAC,oBAAoB,CAOlC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,EAAE,iBAAiB,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,gBAAgB,EAAE;CA2ClF"}
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SoilMoistureConverter = void 0;
4
+ const sensor_1 = require("@bradtech/sensor");
5
+ /**
6
+ * Multi-depth Soil Moisture & Volumetric Water Content (VWC %) Converter.
7
+ *
8
+ * Implements:
9
+ * - Texture-specific linear regression calibration curves (Sand, Loam, Clay, Silt, Peat):
10
+ * $$VWC = a \cdot \text{raw} + b$$
11
+ * - Custom plot-specific laboratory calibration model overrides ($y = \text{slope} \cdot x + \text{intercept}$).
12
+ * - Dielectric permittivity temperature compensation normalized to 20°C:
13
+ * $$VWC_{comp} = VWC - (T_{soil} - 20) \cdot 0.04$$
14
+ */
15
+ class SoilMoistureConverter extends sensor_1.BaseSensorConverter {
16
+ constructor() {
17
+ super(...arguments);
18
+ /** Sensor domain family classification */
19
+ this.sensorFamily = 'soil';
20
+ /** Unique algorithmic model code */
21
+ this.modelCode = 'soil-vwc-texture-calibrated';
22
+ /** Algorithm semver version */
23
+ this.modelVersion = '1.0.0';
24
+ /** Human-readable model description */
25
+ this.description = 'Texture-calibrated multi-depth soil volumetric water content converter';
26
+ }
27
+ /**
28
+ * Converts raw capacitive probe readings into physical volumetric water content percentage (% VWC).
29
+ *
30
+ * @param raw - Raw sensor reading containing depth and uncalibrated value.
31
+ * @param context - Optional context providing soil texture classification or custom laboratory calibration models.
32
+ * @returns Array containing the calibrated soil moisture metric for the corresponding depth.
33
+ */
34
+ convert(raw, context) {
35
+ var _a;
36
+ const textureKey = (context === null || context === void 0 ? void 0 : context.soilTexture) || 'default';
37
+ const presetCoeff = SoilMoistureConverter.TEXTURE_COEFFICIENTS[textureKey] || SoilMoistureConverter.TEXTURE_COEFFICIENTS.default;
38
+ // Check for plot-specific custom linear regression calibration model: y = slope * x + intercept
39
+ const customRegression = (context === null || context === void 0 ? void 0 : context.soilLinearRegression) || ((_a = context === null || context === void 0 ? void 0 : context.calibration) === null || _a === void 0 ? void 0 : _a.linearRegression);
40
+ const slope = typeof (customRegression === null || customRegression === void 0 ? void 0 : customRegression.slope) === 'number' ? customRegression.slope : presetCoeff.slope;
41
+ const intercept = typeof (customRegression === null || customRegression === void 0 ? void 0 : customRegression.intercept) === 'number' ? customRegression.intercept : presetCoeff.intercept;
42
+ const calibrationType = customRegression ? 'custom_linear_regression' : 'texture_preset';
43
+ // Apply linear regression calibration: VWC (%) = a * raw + b
44
+ let vwc = raw.rawValue * slope + intercept;
45
+ // Apply temperature compensation if soil temperature is known (standard reference at 20°C)
46
+ if (raw.soilTemperature !== undefined) {
47
+ const tempDelta = raw.soilTemperature - 20.0;
48
+ // Dielectric permittivity of water decreases ~0.4%/°C
49
+ vwc = vwc - tempDelta * 0.04;
50
+ }
51
+ // Clamp between 0% (oven dry) and 100% (free water saturation)
52
+ const clamped = this.clampWithConfidence(vwc, 0, 100, 0, 70);
53
+ const depth = raw.depthCm || 10;
54
+ return [
55
+ {
56
+ metric: `okf:soil/moisture/${depth}cm`,
57
+ value: clamped.value,
58
+ unit: '%',
59
+ qudtUri: 'qudt:unit/PERCENT',
60
+ confidence: clamped.confidence,
61
+ metadata: {
62
+ depthCm: depth,
63
+ soilTexture: textureKey,
64
+ calibrationType,
65
+ calibrationSlope: slope,
66
+ calibrationIntercept: intercept,
67
+ customModelLabel: customRegression === null || customRegression === void 0 ? void 0 : customRegression.modelLabel,
68
+ rawInput: raw.rawValue,
69
+ },
70
+ },
71
+ ];
72
+ }
73
+ }
74
+ exports.SoilMoistureConverter = SoilMoistureConverter;
75
+ /** Standard USDA soil texture regression coefficients [slope a, intercept b] */
76
+ SoilMoistureConverter.TEXTURE_COEFFICIENTS = {
77
+ sand: { slope: 0.92, intercept: -0.5 },
78
+ loam: { slope: 1.0, intercept: 0.0 },
79
+ clay: { slope: 1.15, intercept: 1.2 },
80
+ silt: { slope: 1.05, intercept: 0.5 },
81
+ peat: { slope: 1.25, intercept: 2.0 },
82
+ default: { slope: 1.0, intercept: 0.0 },
83
+ };
84
+ //# sourceMappingURL=SoilMoistureConverter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SoilMoistureConverter.js","sourceRoot":"","sources":["../src/SoilMoistureConverter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import { BaseSensorConverter, type ConversionContext, type ConversionOutput } from '@bradtech/sensor';
2
+ /**
3
+ * Raw input reading for depth-specific soil temperature.
4
+ */
5
+ export interface SoilTemperatureInput {
6
+ /** Sensor depth probe location in centimeters (e.g. 10, 20, 30 cm) */
7
+ depthCm: number;
8
+ /** Soil temperature in °C */
9
+ temperature: number;
10
+ }
11
+ /**
12
+ * Multi-depth Soil Temperature Profile Converter.
13
+ * Normalizes multi-depth soil thermistor measurements with agronomic plausibility clamping.
14
+ */
15
+ export declare class SoilTemperatureConverter extends BaseSensorConverter<SoilTemperatureInput> {
16
+ /** Sensor domain family classification */
17
+ readonly sensorFamily = "soil";
18
+ /** Unique algorithmic model code */
19
+ readonly modelCode = "soil-temperature-evaluator";
20
+ /** Algorithm semver version */
21
+ readonly modelVersion = "1.0.0";
22
+ /** Human-readable model description */
23
+ readonly description = "Multi-depth soil profile temperature converter";
24
+ /**
25
+ * Converts raw soil temperature into depth-tagged physical metric records.
26
+ *
27
+ * @param raw - Object containing probe depth and measured temperature.
28
+ * @param _context - Optional environmental context.
29
+ * @returns Array containing the depth-tagged soil temperature metric.
30
+ */
31
+ convert(raw: SoilTemperatureInput, _context?: ConversionContext): ConversionOutput[];
32
+ }
33
+ //# sourceMappingURL=SoilTemperatureConverter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SoilTemperatureConverter.d.ts","sourceRoot":"","sources":["../src/SoilTemperatureConverter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAErG;;GAEG;AACH,MAAM,WAAW,oBAAoB;IAClC,sEAAsE;IACtE,OAAO,EAAE,MAAM,CAAA;IACf,6BAA6B;IAC7B,WAAW,EAAE,MAAM,CAAA;CACrB;AAED;;;GAGG;AACH,qBAAa,wBAAyB,SAAQ,mBAAmB,CAAC,oBAAoB,CAAC;IACpF,0CAA0C;IAC1C,QAAQ,CAAC,YAAY,UAAS;IAC9B,oCAAoC;IACpC,QAAQ,CAAC,SAAS,gCAA+B;IACjD,+BAA+B;IAC/B,QAAQ,CAAC,YAAY,WAAU;IAC/B,uCAAuC;IACvC,QAAQ,CAAC,WAAW,oDAAmD;IAEvE;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,gBAAgB,EAAE;CAetF"}
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SoilTemperatureConverter = void 0;
4
+ const sensor_1 = require("@bradtech/sensor");
5
+ /**
6
+ * Multi-depth Soil Temperature Profile Converter.
7
+ * Normalizes multi-depth soil thermistor measurements with agronomic plausibility clamping.
8
+ */
9
+ class SoilTemperatureConverter extends sensor_1.BaseSensorConverter {
10
+ constructor() {
11
+ super(...arguments);
12
+ /** Sensor domain family classification */
13
+ this.sensorFamily = 'soil';
14
+ /** Unique algorithmic model code */
15
+ this.modelCode = 'soil-temperature-evaluator';
16
+ /** Algorithm semver version */
17
+ this.modelVersion = '1.0.0';
18
+ /** Human-readable model description */
19
+ this.description = 'Multi-depth soil profile temperature converter';
20
+ }
21
+ /**
22
+ * Converts raw soil temperature into depth-tagged physical metric records.
23
+ *
24
+ * @param raw - Object containing probe depth and measured temperature.
25
+ * @param _context - Optional environmental context.
26
+ * @returns Array containing the depth-tagged soil temperature metric.
27
+ */
28
+ convert(raw, _context) {
29
+ const depth = raw.depthCm || 10;
30
+ const clamped = this.clampWithConfidence(raw.temperature, -20, 60, -10, 45);
31
+ return [
32
+ {
33
+ metric: `okf:soil/temperature/${depth}cm`,
34
+ value: clamped.value,
35
+ unit: '°C',
36
+ qudtUri: 'qudt:unit/DEG_C',
37
+ confidence: clamped.confidence,
38
+ metadata: { depthCm: depth },
39
+ },
40
+ ];
41
+ }
42
+ }
43
+ exports.SoilTemperatureConverter = SoilTemperatureConverter;
44
+ //# sourceMappingURL=SoilTemperatureConverter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SoilTemperatureConverter.js","sourceRoot":"","sources":["../src/SoilTemperatureConverter.ts"],"names":[],"mappings":";;;AAAA,6CAAqG;AAYrG;;;GAGG;AACH,MAAa,wBAAyB,SAAQ,4BAAyC;IAAvF;;QACG,0CAA0C;QACjC,iBAAY,GAAG,MAAM,CAAA;QAC9B,oCAAoC;QAC3B,cAAS,GAAG,4BAA4B,CAAA;QACjD,+BAA+B;QACtB,iBAAY,GAAG,OAAO,CAAA;QAC/B,uCAAuC;QAC9B,gBAAW,GAAG,gDAAgD,CAAA;IAwB1E,CAAC;IAtBE;;;;;;OAMG;IACH,OAAO,CAAC,GAAyB,EAAE,QAA4B;QAC5D,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,IAAI,EAAE,CAAA;QAC/B,MAAM,OAAO,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QAE3E,OAAO;YACJ;gBACG,MAAM,EAAE,wBAAwB,KAAK,IAAI;gBACzC,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,IAAI,EAAE,IAAI;gBACV,OAAO,EAAE,iBAAiB;gBAC1B,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,QAAQ,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE;aAC9B;SACH,CAAA;IACJ,CAAC;CACH;AAhCD,4DAgCC"}
@@ -0,0 +1,41 @@
1
+ import { BaseSensorConverter, type ConversionContext, type ConversionOutput } from '@bradtech/sensor';
2
+ /**
3
+ * Input parameters for matric water potential and pF calculation.
4
+ */
5
+ export interface SoilWaterPotentialInput {
6
+ /** Sensor depth probe location in centimeters */
7
+ depthCm: number;
8
+ /** Volumetric water content in % (0 - 100) */
9
+ vwcPercent: number;
10
+ }
11
+ /**
12
+ * Soil Matric Water Potential ($\Psi_m$ in kPa) & pF Retention Converter.
13
+ *
14
+ * Implements the Mualem-van Genuchten hydraulic retention model to relate volumetric
15
+ * water content ($\theta$) to matric suction pressure head ($h$ in cm of water) and logarithmic pF scale:
16
+ *
17
+ * $$S_e = \frac{\theta - \theta_r}{\theta_s - \theta_r}, \quad m = 1 - \frac{1}{n}$$
18
+ * $$h = \frac{1}{\alpha} \left(S_e^{-1/m} - 1\right)^{1/n}, \quad \Psi_m = -h \cdot 0.0980665 \text{ kPa}$$
19
+ * $$pF = \log_{10}(h_{cm})$$
20
+ */
21
+ export declare class SoilWaterPotentialConverter extends BaseSensorConverter<SoilWaterPotentialInput> {
22
+ /** Sensor domain family classification */
23
+ readonly sensorFamily = "soil";
24
+ /** Unique algorithmic model code */
25
+ readonly modelCode = "soil-water-potential-van-genuchten";
26
+ /** Algorithm semver version */
27
+ readonly modelVersion = "1.0.0";
28
+ /** Human-readable model description */
29
+ readonly description = "Van Genuchten soil water retention and pF water potential calculator";
30
+ /** Van Genuchten soil hydraulic retention parameters [$\alpha$, $n$, $\theta_r$, $\theta_s$] */
31
+ private static SOIL_VG_PARAMS;
32
+ /**
33
+ * Converts volumetric water content percentage into matric water potential (kPa) and pF availability scale.
34
+ *
35
+ * @param raw - Object containing probe depth and VWC percentage.
36
+ * @param context - Optional context specifying soil textural class.
37
+ * @returns Array containing both matric potential (kPa) and pF metrics.
38
+ */
39
+ convert(raw: SoilWaterPotentialInput, context?: ConversionContext): ConversionOutput[];
40
+ }
41
+ //# sourceMappingURL=SoilWaterPotentialConverter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SoilWaterPotentialConverter.d.ts","sourceRoot":"","sources":["../src/SoilWaterPotentialConverter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAErG;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACrC,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAA;IACf,8CAA8C;IAC9C,UAAU,EAAE,MAAM,CAAA;CACpB;AAED;;;;;;;;;GASG;AACH,qBAAa,2BAA4B,SAAQ,mBAAmB,CAAC,uBAAuB,CAAC;IAC1F,0CAA0C;IAC1C,QAAQ,CAAC,YAAY,UAAS;IAC9B,oCAAoC;IACpC,QAAQ,CAAC,SAAS,wCAAuC;IACzD,+BAA+B;IAC/B,QAAQ,CAAC,YAAY,WAAU;IAC/B,uCAAuC;IACvC,QAAQ,CAAC,WAAW,0EAAyE;IAE7F,gGAAgG;IAChG,OAAO,CAAC,MAAM,CAAC,cAAc,CAM5B;IAED;;;;;;OAMG;IACH,OAAO,CAAC,GAAG,EAAE,uBAAuB,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,gBAAgB,EAAE;CA+CxF"}
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SoilWaterPotentialConverter = void 0;
4
+ const sensor_1 = require("@bradtech/sensor");
5
+ /**
6
+ * Soil Matric Water Potential ($\Psi_m$ in kPa) & pF Retention Converter.
7
+ *
8
+ * Implements the Mualem-van Genuchten hydraulic retention model to relate volumetric
9
+ * water content ($\theta$) to matric suction pressure head ($h$ in cm of water) and logarithmic pF scale:
10
+ *
11
+ * $$S_e = \frac{\theta - \theta_r}{\theta_s - \theta_r}, \quad m = 1 - \frac{1}{n}$$
12
+ * $$h = \frac{1}{\alpha} \left(S_e^{-1/m} - 1\right)^{1/n}, \quad \Psi_m = -h \cdot 0.0980665 \text{ kPa}$$
13
+ * $$pF = \log_{10}(h_{cm})$$
14
+ */
15
+ class SoilWaterPotentialConverter extends sensor_1.BaseSensorConverter {
16
+ constructor() {
17
+ super(...arguments);
18
+ /** Sensor domain family classification */
19
+ this.sensorFamily = 'soil';
20
+ /** Unique algorithmic model code */
21
+ this.modelCode = 'soil-water-potential-van-genuchten';
22
+ /** Algorithm semver version */
23
+ this.modelVersion = '1.0.0';
24
+ /** Human-readable model description */
25
+ this.description = 'Van Genuchten soil water retention and pF water potential calculator';
26
+ }
27
+ /**
28
+ * Converts volumetric water content percentage into matric water potential (kPa) and pF availability scale.
29
+ *
30
+ * @param raw - Object containing probe depth and VWC percentage.
31
+ * @param context - Optional context specifying soil textural class.
32
+ * @returns Array containing both matric potential (kPa) and pF metrics.
33
+ */
34
+ convert(raw, context) {
35
+ const textureKey = (context === null || context === void 0 ? void 0 : context.soilTexture) || 'default';
36
+ const params = SoilWaterPotentialConverter.SOIL_VG_PARAMS[textureKey] || SoilWaterPotentialConverter.SOIL_VG_PARAMS.default;
37
+ const theta = Math.min(params.thetaS - 0.1, Math.max(params.thetaR + 0.1, raw.vwcPercent));
38
+ const Se = (theta - params.thetaR) / (params.thetaS - params.thetaR); // Effective saturation
39
+ const m = 1.0 - 1.0 / params.n;
40
+ // Matric suction head (h in cm of water column): h = (1/alpha) * (Se^(-1/m) - 1)^(1/n)
41
+ const hCm = (1.0 / params.alpha) * Math.pow(Math.pow(Se, -1.0 / m) - 1.0, 1.0 / params.n);
42
+ // Convert head cm to matric potential in kPa: 1 cm H2O ≈ 0.0980665 kPa
43
+ const potentialKpa = -(hCm * 0.0980665);
44
+ // pF = log10(h in cm)
45
+ const pF = Math.log10(Math.max(1.0, hCm));
46
+ const pFClamped = this.clampWithConfidence(pF, 0, 7.0);
47
+ const kpaClamped = this.clampWithConfidence(potentialKpa, -2000, 0);
48
+ const depth = raw.depthCm || 10;
49
+ return [
50
+ {
51
+ metric: `okf:soil/potential/matric/${depth}cm`,
52
+ value: kpaClamped.value,
53
+ unit: 'kPa',
54
+ qudtUri: 'qudt:unit/KiloPA',
55
+ confidence: pFClamped.confidence,
56
+ metadata: {
57
+ depthCm: depth,
58
+ soilTexture: textureKey,
59
+ },
60
+ },
61
+ {
62
+ metric: `okf:soil/potential/pf/${depth}cm`,
63
+ value: pFClamped.value,
64
+ unit: 'pF',
65
+ qudtUri: 'qudt:unit/UNITLESS',
66
+ confidence: pFClamped.confidence,
67
+ metadata: {
68
+ depthCm: depth,
69
+ soilTexture: textureKey,
70
+ waterAvailabilityState: pF < 2.0 ? 'saturated' : pF < 2.5 ? 'field_capacity' : pF < 3.8 ? 'readily_available' : pF < 4.2 ? 'water_stress' : 'wilting_point',
71
+ },
72
+ },
73
+ ];
74
+ }
75
+ }
76
+ exports.SoilWaterPotentialConverter = SoilWaterPotentialConverter;
77
+ /** Van Genuchten soil hydraulic retention parameters [$\alpha$, $n$, $\theta_r$, $\theta_s$] */
78
+ SoilWaterPotentialConverter.SOIL_VG_PARAMS = {
79
+ sand: { alpha: 0.145, n: 2.68, thetaR: 4.5, thetaS: 43.0 },
80
+ loam: { alpha: 0.036, n: 1.56, thetaR: 7.8, thetaS: 43.0 },
81
+ clay: { alpha: 0.008, n: 1.09, thetaR: 6.8, thetaS: 38.0 },
82
+ silt: { alpha: 0.016, n: 1.37, thetaR: 3.4, thetaS: 46.0 },
83
+ default: { alpha: 0.036, n: 1.56, thetaR: 7.8, thetaS: 43.0 },
84
+ };
85
+ //# sourceMappingURL=SoilWaterPotentialConverter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SoilWaterPotentialConverter.js","sourceRoot":"","sources":["../src/SoilWaterPotentialConverter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export * from './SoilMoistureConverter';
2
+ export * from './SoilWaterPotentialConverter';
3
+ export * from './SoilTemperatureConverter';
4
+ export * from './SoilElectricalConductivityConverter';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAA;AACvC,cAAc,+BAA+B,CAAA;AAC7C,cAAc,4BAA4B,CAAA;AAC1C,cAAc,uCAAuC,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./SoilMoistureConverter"), exports);
18
+ __exportStar(require("./SoilWaterPotentialConverter"), exports);
19
+ __exportStar(require("./SoilTemperatureConverter"), exports);
20
+ __exportStar(require("./SoilElectricalConductivityConverter"), exports);
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,0DAAuC;AACvC,gEAA6C;AAC7C,6DAA0C;AAC1C,wEAAqD"}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@bradtech/sensor-soil",
3
+ "version": "1.1.0",
4
+ "description": "Multi-depth capacitive volumetric water content, soil matric water potential pF, EC, and soil temperature converters",
5
+ "author": "Olivier Lépine <olivier@lepine.fr>",
6
+ "license": "AGPL-3.0-or-later",
7
+ "copyright": "Copyright (C) 2026 Olivier Lépine",
8
+ "publishConfig": {
9
+ "access": "public",
10
+ "registry": "https://registry.npmjs.org/"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/bradtech-oss/stack.git",
15
+ "directory": "packages/sensor-soil"
16
+ },
17
+ "keywords": [
18
+ "iot",
19
+ "soil-moisture",
20
+ "vwc",
21
+ "matric-potential",
22
+ "pf-curve",
23
+ "van-genuchten",
24
+ "soil-ec",
25
+ "soil-temperature",
26
+ "agronomy",
27
+ "irrigation",
28
+ "brad",
29
+ "agpl-v3"
30
+ ],
31
+ "main": "./dist/index.js",
32
+ "module": "./dist/index.js",
33
+ "types": "./dist/index.d.ts",
34
+ "exports": {
35
+ ".": {
36
+ "bun": "./src/index.ts",
37
+ "import": "./dist/index.js",
38
+ "require": "./dist/index.js",
39
+ "types": "./dist/index.d.ts",
40
+ "default": "./dist/index.js"
41
+ }
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "src",
46
+ "LICENSE",
47
+ "README.md",
48
+ "HOWTO.md",
49
+ "!**/*.test.ts"
50
+ ],
51
+ "scripts": {
52
+ "clean": "rm -rf dist",
53
+ "build": "tsc",
54
+ "wbuild": "tsc --watch",
55
+ "test": "bun test"
56
+ },
57
+ "dependencies": {
58
+ "@bradtech/sensor": "workspace:*"
59
+ },
60
+ "devDependencies": {
61
+ "@tsconfig/recommended": "^1.0.1",
62
+ "@types/node": "^22.10.1",
63
+ "typescript": "^5.1.5"
64
+ }
65
+ }
@@ -0,0 +1,63 @@
1
+ import { BaseSensorConverter, type ConversionContext, type ConversionOutput } from '@bradtech/sensor'
2
+
3
+ /**
4
+ * Raw input reading for soil bulk electrical conductivity.
5
+ */
6
+ export interface SoilEcInput {
7
+ /** Sensor depth probe location in centimeters */
8
+ depthCm: number
9
+ /** Bulk electrical conductivity reading in mS/cm ($dS/m$) */
10
+ bulkEcMsCm: number
11
+ /** Soil temperature at probe depth in °C */
12
+ soilTemperature?: number
13
+ }
14
+
15
+ /**
16
+ * Soil Bulk Electrical Conductivity & Salinity Converter.
17
+ *
18
+ * Normalizes bulk EC readings to the international agronomic standard reference temperature of 25°C ($EC_{25}$):
19
+ * $$EC_{25} = \frac{EC_T}{1 + 0.019 \cdot (T_{soil} - 25)}$$
20
+ */
21
+ export class SoilElectricalConductivityConverter extends BaseSensorConverter<SoilEcInput> {
22
+ /** Sensor domain family classification */
23
+ readonly sensorFamily = 'soil'
24
+ /** Unique algorithmic model code */
25
+ readonly modelCode = 'soil-ec-temperature-normalized'
26
+ /** Algorithm semver version */
27
+ readonly modelVersion = '1.0.0'
28
+ /** Human-readable model description */
29
+ readonly description = 'Temperature-normalized soil electrical conductivity converter'
30
+
31
+ /**
32
+ * Converts raw bulk EC into normalized 25°C salinity metrics with agronomic salinity classification.
33
+ *
34
+ * @param raw - Object containing probe depth, measured EC, and soil temperature.
35
+ * @param _context - Optional environmental context.
36
+ * @returns Array containing the temperature-compensated EC metric in mS/cm.
37
+ */
38
+ convert(raw: SoilEcInput, _context?: ConversionContext): ConversionOutput[] {
39
+ let ec25 = raw.bulkEcMsCm
40
+
41
+ // Temperature compensation: EC_25 = EC_T / (1 + 0.019 * (T - 25))
42
+ if (raw.soilTemperature !== undefined) {
43
+ ec25 = raw.bulkEcMsCm / (1.0 + 0.019 * (raw.soilTemperature - 25.0))
44
+ }
45
+
46
+ const clamped = this.clampWithConfidence(ec25, 0, 20, 0, 10)
47
+ const depth = raw.depthCm || 10
48
+
49
+ return [
50
+ {
51
+ metric: `okf:soil/conductivity/ec/${depth}cm`,
52
+ value: clamped.value,
53
+ unit: 'mS/cm',
54
+ qudtUri: 'qudt:unit/MilliS-PER-M',
55
+ confidence: clamped.confidence,
56
+ metadata: {
57
+ depthCm: depth,
58
+ salinityClass: ec25 < 2.0 ? 'non_saline' : ec25 < 4.0 ? 'slightly_saline' : 'moderately_saline',
59
+ },
60
+ },
61
+ ]
62
+ }
63
+ }