@bradtech/sensor-air 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.
- package/HOWTO.md +76 -0
- package/LICENSE +620 -0
- package/README.md +27 -0
- package/dist/CanopyMicroclimateConverter.d.ts +40 -0
- package/dist/CanopyMicroclimateConverter.d.ts.map +1 -0
- package/dist/CanopyMicroclimateConverter.js +92 -0
- package/dist/CanopyMicroclimateConverter.js.map +1 -0
- package/dist/EvapotranspirationConverter.d.ts +45 -0
- package/dist/EvapotranspirationConverter.d.ts.map +1 -0
- package/dist/EvapotranspirationConverter.js +61 -0
- package/dist/EvapotranspirationConverter.js.map +1 -0
- package/dist/GroundFrostRiskConverter.d.ts +38 -0
- package/dist/GroundFrostRiskConverter.d.ts.map +1 -0
- package/dist/GroundFrostRiskConverter.js +73 -0
- package/dist/GroundFrostRiskConverter.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
- package/src/CanopyMicroclimateConverter.ts +104 -0
- package/src/EvapotranspirationConverter.ts +77 -0
- package/src/GroundFrostRiskConverter.ts +79 -0
- package/src/index.ts +3 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { BaseSensorConverter, type ConversionContext, type ConversionOutput } from '@bradtech/sensor'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Daily temperature and radiation inputs required for reference evapotranspiration computation.
|
|
5
|
+
*/
|
|
6
|
+
export interface EvapotranspirationInput {
|
|
7
|
+
/** Daily minimum canopy temperature in °C ($T_{min}$) */
|
|
8
|
+
tempMin: number
|
|
9
|
+
/** Daily maximum canopy temperature in °C ($T_{max}$) */
|
|
10
|
+
tempMax: number
|
|
11
|
+
/** Daily mean canopy temperature in °C ($T_{mean}$) */
|
|
12
|
+
tempMean: number
|
|
13
|
+
/** Global extraterrestrial or measured solar radiation flux in $\text{MJ}/(\text{m}^2\cdot\text{day})$ ($R_a$) */
|
|
14
|
+
solarRadiationMj?: number
|
|
15
|
+
/** Geographic plot latitude in decimal degrees (e.g. 44.8378 for Bordeaux) */
|
|
16
|
+
latitudeDegrees?: number
|
|
17
|
+
/** Julian day of year ($1 - 366$) */
|
|
18
|
+
dayOfYear?: number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Agronomic Daily Reference Evapotranspiration ($ET_0$) Converter.
|
|
23
|
+
*
|
|
24
|
+
* Implements the internationally recognized FAO-56 Hargreaves-Samani temperature-radiation formulation:
|
|
25
|
+
* $$ET_0 = 0.0023 \cdot (T_{mean} + 17.8) \cdot \sqrt{T_{max} - T_{min}} \cdot R_a \cdot 0.408$$
|
|
26
|
+
*
|
|
27
|
+
* where $0.408$ represents the inverse of the latent heat of vaporization ($\lambda^{-1}$ in $\text{mm}/(\text{MJ}\cdot\text{m}^{-2})$).
|
|
28
|
+
*/
|
|
29
|
+
export class EvapotranspirationConverter extends BaseSensorConverter<EvapotranspirationInput> {
|
|
30
|
+
/** Sensor domain family classification */
|
|
31
|
+
readonly sensorFamily = 'air'
|
|
32
|
+
/** Unique algorithmic model code */
|
|
33
|
+
readonly modelCode = 'evapotranspiration-hargreaves-fao56'
|
|
34
|
+
/** Algorithm semver version */
|
|
35
|
+
readonly modelVersion = '1.0.0'
|
|
36
|
+
/** Human-readable model description */
|
|
37
|
+
readonly description = 'FAO-56 Hargreaves-Samani daily reference evapotranspiration (ET0) calculator'
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Converts daily temperature extremes and radiation into daily reference crop evapotranspiration ($ET_0$ in mm/day).
|
|
41
|
+
*
|
|
42
|
+
* @param raw - Object containing $T_{min}, T_{max}, T_{mean}$, and optional radiation $R_a$.
|
|
43
|
+
* @param _context - Optional environmental context.
|
|
44
|
+
* @returns Single-element array containing the $ET_0$ metric in mm/day.
|
|
45
|
+
*/
|
|
46
|
+
convert(raw: EvapotranspirationInput, _context?: ConversionContext): ConversionOutput[] {
|
|
47
|
+
const deltaT = Math.max(0, raw.tempMax - raw.tempMin)
|
|
48
|
+
const tMean = raw.tempMean
|
|
49
|
+
|
|
50
|
+
// Extraterrestrial radiation estimate (Ra) if not provided directly
|
|
51
|
+
let Ra = raw.solarRadiationMj
|
|
52
|
+
if (!Ra || Ra <= 0) {
|
|
53
|
+
// Default approximation for temperate European agricultural latitudes (~44°N)
|
|
54
|
+
Ra = 25.0
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Hargreaves-Samani equation: ET0 = 0.0023 * (Tmean + 17.8) * sqrt(Tmax - Tmin) * Ra * 0.408
|
|
58
|
+
const et0 = 0.0023 * (tMean + 17.8) * Math.sqrt(deltaT) * Ra * 0.408
|
|
59
|
+
|
|
60
|
+
const clamped = this.clampWithConfidence(et0, 0, 15, 0, 10)
|
|
61
|
+
|
|
62
|
+
return [
|
|
63
|
+
{
|
|
64
|
+
metric: 'okf:agronomy/evapotranspiration/et0',
|
|
65
|
+
value: clamped.value,
|
|
66
|
+
unit: 'mm/day',
|
|
67
|
+
qudtUri: 'qudt:unit/MilliM',
|
|
68
|
+
confidence: clamped.confidence,
|
|
69
|
+
metadata: {
|
|
70
|
+
method: 'Hargreaves-Samani-FAO56',
|
|
71
|
+
tempRange: Number(deltaT.toFixed(2)),
|
|
72
|
+
radiationMj: Ra,
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { BaseSensorConverter, type ConversionContext, type ConversionOutput } from '@bradtech/sensor'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Microclimate temperature and relative humidity inputs for frost risk evaluation.
|
|
5
|
+
*/
|
|
6
|
+
export interface GroundFrostInput {
|
|
7
|
+
/** Air temperature measured at plant/canopy height in °C ($T$) */
|
|
8
|
+
canopyTemperature: number
|
|
9
|
+
/** Relative humidity measured at plant/canopy height in % ($RH$) */
|
|
10
|
+
canopyHumidity: number
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Radiative Ground Frost Risk & Wet-Bulb Temperature Converter.
|
|
15
|
+
*
|
|
16
|
+
* Implements the Roland Stull empirical formulation to compute the Psychrometric Wet-Bulb
|
|
17
|
+
* Temperature ($T_w$ in °C), which is the exact physical temperature reached by plant leaves
|
|
18
|
+
* and vegetative buds under radiative night cooling:
|
|
19
|
+
*
|
|
20
|
+
* $$T_w = T \cdot \arctan\left(0.151977 \cdot (RH + 8.313659)^{1/2}\right) + \arctan(T + RH) - \arctan(RH - 1.676331) + 0.00391838 \cdot RH^{3/2} \cdot \arctan(0.023101 \cdot RH) - 4.686035$$
|
|
21
|
+
*/
|
|
22
|
+
export class GroundFrostRiskConverter extends BaseSensorConverter<GroundFrostInput> {
|
|
23
|
+
/** Sensor domain family classification */
|
|
24
|
+
readonly sensorFamily = 'air'
|
|
25
|
+
/** Unique algorithmic model code */
|
|
26
|
+
readonly modelCode = 'ground-frost-risk-evaluator'
|
|
27
|
+
/** Algorithm semver version */
|
|
28
|
+
readonly modelVersion = '1.0.0'
|
|
29
|
+
/** Human-readable model description */
|
|
30
|
+
readonly description = 'Radiative ground frost risk and wet-bulb temperature calculator'
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Converts canopy microclimate readings into wet-bulb temperature ($T_w$) and discrete agronomic frost severity index.
|
|
34
|
+
*
|
|
35
|
+
* @param raw - Object containing canopy temperature and relative humidity.
|
|
36
|
+
* @param _context - Optional environmental conversion context.
|
|
37
|
+
* @returns Array containing the wet-bulb temperature and the discrete frost risk index (0 to 3).
|
|
38
|
+
*/
|
|
39
|
+
convert(raw: GroundFrostInput, _context?: ConversionContext): ConversionOutput[] {
|
|
40
|
+
const T = raw.canopyTemperature
|
|
41
|
+
const RH = raw.canopyHumidity
|
|
42
|
+
|
|
43
|
+
// Stull formula for Wet Bulb Temperature (Tw in °C):
|
|
44
|
+
const Tw =
|
|
45
|
+
T * Math.atan(0.151977 * Math.pow(RH + 8.313659, 0.5)) +
|
|
46
|
+
Math.atan(T + RH) -
|
|
47
|
+
Math.atan(RH - 1.676331) +
|
|
48
|
+
0.00391838 * Math.pow(RH, 1.5) * Math.atan(0.023101 * RH) -
|
|
49
|
+
4.686035
|
|
50
|
+
|
|
51
|
+
// Frost Risk Level: 0 (No risk), 1 (Monitoring, Tw < 3°C), 2 (Warning, Tw < 1°C), 3 (Severe Frost, Tw < 0°C)
|
|
52
|
+
let frostRisk = 0
|
|
53
|
+
if (Tw <= -0.5) frostRisk = 3
|
|
54
|
+
else if (Tw <= 1.0) frostRisk = 2
|
|
55
|
+
else if (Tw <= 3.0) frostRisk = 1
|
|
56
|
+
|
|
57
|
+
const twClamped = this.clampWithConfidence(Tw, -40, 50)
|
|
58
|
+
|
|
59
|
+
return [
|
|
60
|
+
{
|
|
61
|
+
metric: 'okf:agronomy/microclimate/wet_bulb_temperature',
|
|
62
|
+
value: twClamped.value,
|
|
63
|
+
unit: '°C',
|
|
64
|
+
qudtUri: 'qudt:unit/DEG_C',
|
|
65
|
+
confidence: twClamped.confidence,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
metric: 'okf:agronomy/risk/frost_index',
|
|
69
|
+
value: frostRisk,
|
|
70
|
+
unit: 'level',
|
|
71
|
+
qudtUri: 'qudt:unit/UNITLESS',
|
|
72
|
+
confidence: 1.0,
|
|
73
|
+
metadata: {
|
|
74
|
+
frostState: frostRisk === 3 ? 'severe' : frostRisk === 2 ? 'warning' : frostRisk === 1 ? 'watch' : 'none',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/index.ts
ADDED