-
Notifications
You must be signed in to change notification settings - Fork 1
/
tracer-mobile.cr1
307 lines (273 loc) · 10.3 KB
/
tracer-mobile.cr1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Wheat pile emission scoping study May 2018
'
' Source code for mobile station (plume) logger. Responsible for:
' * TSI DustTrak II - aerosol monitor
' * LICOR Biosciences LI-840A - CO2 analyzer
' * Airmar Tech 150WX - compact weather station + GPS
'
' Copyright 2018 :: Patrick O'Keeffe
' Laboratory for Atmospheric Research at Washington State University
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
ConstTable
Const UTC_OFFSET = -7 'Pacific Daylight Time
Const DAYS_OF_DATA = 7
EndConstTable
'-------------- NO USER SERVICEABLE PARTS BELOW THIS LINE ------------
Const NTP_SERVER = "1.us.pool.ntp.org"
Const MAX_CLOCK_DRIFT = 500 'ms
Public ntp_offset
Units ntp_offset = ms
Const LI840A_COM = Com1 'C1/C2
Const LI840A_BAUD = 9600
Const LI840A_FMT = 3 '8/n/1, RS232 logic
Const LI840A_REC = 120
Const LI840A_BUFF = LI840A_REC*2 + 1
Const WX_COM = Com2 'C3/C4
Const WX_BAUD = 4800
Const WX_FMT = 3 '8/n/1, RS232
Const WX_BUFF = 250
Const DOLLAR_W = &h2457 ' "$W"
Const DOLLAR_G = &h2447 ' "$G"
Const CRLF = &h0D0A ' <CR><LF>
Const DF_TSI_PM = 1
Const TSI_PM_OFF = 0.0103 'XXXX unit-specific calibration offset (sn 8530152108)
Const TSI_PM_MLT = (1.0 - TSI_PM_OFF)/5000 '0-1.0 mg/m^3, 0-5V
'Const TSI_PM_OFF = 0.008 'XXXX unit-specific calibration offset (sn 8530150710)
'Const TSI_PM_MLT = (1.0 - TSI_PM_OFF)/5000 '0-1.0 mg/m^3, 0-5V
Const DF_LI840A_CO2 = 3
Const LI840A_CO2_OFF = 0 'ppm
Const LI840A_CO2_MLT = (2500 - LI840A_CO2_OFF)/5000 '0-5000ppm, 0-5V
Public li840a_config As String * 300
Public li840a_resp As String * 32
Public li840a_record As String * LI840A_REC
Public li840a(6)
Alias li840a(1) = li840a_cell_T
Alias li840a(2) = li840a_cell_P
Alias li840a(3) = li840a_CO2
Alias li840a(4) = li840a_pwr_src
Alias li840a(5) = li840a_H2O
Alias li840a(6) = li840a_dewpoint
Units li840a_cell_T = degC
Units li840a_cell_P = kPa
Units li840a_CO2 = ppm
Units li840a_pwr_src = Vdc
Units li840a_H2O = ppth
Units li840a_dewpoint = degC
Public li840a_analog_CO2
Units li840a_analog_CO2 = ppm
Public dusttrak2_analog_pm
Units dusttrak2_analog_pm = mg/m^3
'record lengths from Table 1, PB100 WeatherStation Technical Manual r1.007
Public wimda As String * 81+1
Public gprmc As String * 74+1
Public wx_bytes
Public gps_bytes
Public VP, SVP, 'for calcing mean RH
Public metvals(10)
' "IMDA," consumed
'Alias metvals(1) = barometric pressure, inches of mercury
' ",I," consumed
Alias metvals(2) = m150wx_P 'barometric pressure, bars
' ",B," consumed
Alias metvals(3) = m150wx_T 'air temperature, degrees C
' ",C,,," consumed (no water temperature or units)
Alias metvals(4) = m150wx_RH 'relative humidity, %
' ",," consumed(no absolute humidity)
Alias metvals(5) = m150wx_DP 'dewpoint, degrees C
' ",C," consumed
Alias metvals(6) = m150wx_WD 'degrees E of TN
' ",T," consumed
Alias metvals(7) = m150wx_WD_mag 'degrees E of MN
' ",M," consumed
'Alias metvals(8) = wind speed, knots
' ",N," consumed
Alias metvals(9) = m150wx_WS 'm/s
' "M*" consumed
'Alias metvals(10) = checksum
Units m150wx_P = hPa
Units m150wx_T = degC
Units m150wx_RH = %
Units m150wx_DP = degC
Units m150wx_WD = degTN
Units m150wx_WD_mag = degMN
Units m150wx_WS = m/s
'TODO FIXME: in OS 29, the `GPS` instruction gained ability to perform
'time-syncing against only NMEA data (no PPS reqd)... but it appears
'to require the GPGGA sentence. Consider jumpering COM2 to COM1 Rx and
'acquiring GPS data more neatly and also get time updates. May conflict
'with `NetworkTimeProtocol`?
Public gpsstrs(13) As String * 16
' "PRMC" consumed
'Alias gpsstrs(1) = UTC in hhmmss.ss format
'Alias gpsstrs(2) = "A" or "V"
Alias gpsstrs(3) = strLatitude 'HHMM.MMMM
'Alias gpsstrs(4) = "N" or "S"
Alias gpsstrs(5) = strLongitude 'HHMM.MMMM
'Alias gpsstrs(6) = "E" or "W"
Alias gpsstrs(7) = strSOG 'knots
Alias gpsstrs(8) = strCOG 'degrees E of TN
Alias gpsstrs(9) = strDate 'DDMMYY
Alias gpsstrs(10) = strMagVar 'degrees E/W
Alias gpsstrs(11) = strMagVarSign ' "E" or "W"
Alias gpsstrs(12) = strMode ' {A, D, E, M, S, N}
Alias gpsstrs(13) = strChecksum
Public gpsvals(5)
Alias gpsvals(1) = latitude_deg
Alias gpsvals(2) = latitude_min
Alias gpsvals(3) = longitude_deg
Alias gpsvals(4) = longitude_min
Alias gpsvals(5) = mag_variation
Public gps_mode As String * 1
Units latitude_deg = degreesN
Units latitude_min = minutesN
Units longitude_deg = degreesE
Units longitude_min = minutesE
Units mag_variation = degEofTN
Public panel_tmpr
Units panel_tmpr = degC
DataTable(tsdata,True,-1)
DataInterval(0,1,Sec,5)
CardOut(0,86400*DAYS_OF_DATA)
Sample(1,dusttrak2_analog_pm,FP2)
FieldNames("dusttrak2_analog_pm:aerosol concentration")
Sample(1,li840a_CO2,IEEE4)
FieldNames("li840a_CO2:CO2 mixing ratio")
Sample(1,li840a_H2O,IEEE4)
FieldNames("li840a_H2O:H2O mixing ratio")
Sample(1,m150wx_P,IEEE4)
FieldNames("m150wx_P:barometric pressure")
Sample(1,m150wx_T,FP2)
FieldNames("m150wx_T:ambient temperature")
Sample(1,m150wx_RH,FP2)
FieldNames("m150wx_RH:relative humidity")
Sample(1,m150wx_DP,FP2)
FieldNames("m150wx_DP:dewpoint")
Sample(1,m150wx_WS,FP2)
FieldNames("m150wx_WS:wind speed")
Sample(1,m150wx_WD,FP2)
FieldNames("m150wx_WD:wind direction (true north)")
Sample(1,m150wx_WD_mag,FP2)
FieldNames("m150wx_WD_mag:wind direction (magnetic north)")
Sample(1,latitude_deg,FP2)
FieldNames("m150wx_latitude_deg:position latitude degrees component")
Sample(1,latitude_min,IEEE4)
FieldNames("m150wx_latitude_min:position latitude minutes component")
Sample(1,longitude_deg,FP2)
FieldNames("m150wx_longitude_deg:position longitude degrees component")
Sample(1,longitude_min,IEEE4)
FieldNames("m150wx_longitude_min:position longitude minutes component")
Sample(1,mag_variation,FP2)
FieldNames("m150wx_mag_variation:position magnetic variation")
Sample(1,gps_mode,String)
FieldNames("m150wx_gps_mode:status indicator (see manual)")
Sample(1,li840a_analog_CO2,IEEE4)
FieldNames("li840a_analog_CO2:CO2 mixing ratio (analog data)"
EndTable
DataTable(minutely,True,-1)
DataInterval(0,1,Min,1)
CardOut(0,1440*DAYS_OF_DATA)
Sample(1,dusttrak2_analog_pm,FP2)
Average(1,li840a_CO2,IEEE4,li840a_CO2=NAN)
FieldNames("li840a_CO2:CO2 mixing ratio")
Average(1,li840a_H2O,IEEE4,li840a_H2O=NAN)
FieldNames("li840a_H2O:H2O mixing ratio")
Average(1,li840a_cell_T,IEEE4,li840a_cell_T=NAN)
FieldNames("li840a_cell_T:sample cell temperature")
Average(1,li840a_cell_P,IEEE4,li840a_cell_P=NAN)
FieldNames("li840a_cell_P:sample cell pressure")
Average(1,li840a_dewpoint,IEEE4,li840a_dewpoint=NAN)
FieldNames("li840a_dewpoint:H2O dewpoint")
Average(1,li840a_pwr_src,IEEE4,li840a_pwr_src=NAN)
FieldNames("li840a_pwr_src:power input")
Average(1,m150wx_P,IEEE4,m150wx_P=NAN)
FieldNames("m150wx_P:barometric pressure")
Average(1,m150wx_T,FP2,m150wx_T=NAN)
FieldNames("m150wx_T:ambient temperature")
Average(1,m150wx_RH,FP2,m150wx_RH=NAN)
FieldNames("m150wx_RH:relative humidity")
Average(1,m150wx_WS,FP2,m150wx_WS=NAN)
FieldNames("m150wx_WS:wind speed")
Median(1,m150wx_WD,60,FP2,m150wx_WD=NAN)
FieldNames("m150wx_WD:wind direction (true north)")
Median(1,m150wx_WD_mag,60,FP2,m150wx_WD_mag=NAN)
FieldNames("m150wx_WD_mag:wind direction (magnetic north)")
Average(1,li840a_analog_CO2,IEEE4,li840a_analog_CO2=NAN)
FieldNames("li840a_analog_CO2:CO2 mixing ratio (analog data)")
Average(1,panel_tmpr,FP2,panel_tmpr=NAN)
FieldNames("logger_panel_tmpr:logger wiring panel temperature")
EndTable
DisplayMenu("Plume monitoring", -1)
DisplayValue("PM, mg/m^3", dusttrak2_analog_pm)
DisplayValue("CO2, ppm", li840a_CO2)
DisplayValue("WS, m/s", m150wx_WS)
DisplayValue("WD, deg", m150wx_WD)
DisplayValue("Lat. minute", latitude_min)
DisplayValue("Long. minute", longitude_min)
DisplayValue("Logger tmpr", panel_tmpr)
EndMenu
'===========================================================================
BeginProg
SetStatus("StationName", "tracer-mobile")
SerialOpen(WX_COM,WX_BAUD,WX_FMT,0,WX_BUFF)
Move(metvals(1),10,NAN,1)
li840a_config = ("<li840><cfg><outrate>1</outrate></cfg><rs232><co2>true</co2>" & _
"<co2abs>false</co2abs><h2o>true</h2o><h2odewpoint>true</h2odewpoint>" & _
"<h2oabs>false</h2oabs><celltemp>true</celltemp><cellpres>true</cellpres>" & _
"<ivolt>true</ivolt><raw>false</raw><strip>true</strip></rs232></li840>")
li840a_resp = ("<li840><ack>true</ack></li840>")
SerialOpen(LI840A_COM,LI840A_BAUD,LI840A_FMT,0,LI840A_BUFF)
SerialOut(LI840A_COM,li840a_config,li840a_resp,3,1)
Scan (1,Sec,0,0)
VoltDiff(dusttrak2_analog_pm,1,mv5000,DF_TSI_PM,True,0,250,TSI_PM_MLT,TSI_PM_OFF)
VoltDiff(li840a_analog_CO2,1,mv5000,DF_LI840A_CO2,True,0,250,LI840A_CO2_MLT,LI840A_CO2_OFF)
PanelTemp(panel_tmpr, 250)
SerialIn(li840a_record,LI840A_COM,50,&h0A,LI840A_BUFF)
If (Len(li840a_record)) Then
SplitStr(li840a(1),li840a_record,"",6,0)
Else
Move(li840a(1),6,NAN,1)
EndIf
SerialInRecord(WX_COM,wimda,DOLLAR_W,0,CRLF,wx_bytes,110)
SerialInRecord(WX_COM,gprmc,DOLLAR_G,0,CRLF,gps_bytes,110)
If (Len(wimda)) Then
SplitStr(metvals(1),wimda,"",10,0)
m150wx_P *= 1000 'bar --> mbar
VaporPressure(VP,m150wx_T,m150wx_RH)
SatVP(SVP,m150wx_T)
Else
Move(metvals(1),10,NAN,1)
VP = NAN
SVP = NAN
EndIf
If (Len(gprmc)) Then
'HINT cursory tests show numeric parser is faster than character-based
'but unfortunately GPS data must be parsed as strings and therefore
'require character-based
SplitStr(gpsstrs(1),gprmc,",*",13,6)
If (strLatitude="E" OR strLatitude="W") Then
'waiting for signal lock
Move(gpsvals(1),5,NAN,1)
gps_mode = gpsstrs(4)
Else
latitude_deg = Left(strLatitude,2) 'str->int
latitude_min = Right(strLatitude,7)
longitude_deg = Left(strLongitude,3)
longitude_min = Right(strLongitude,7)
mag_variation = strMagVar
'HINT follow TN = MN + MV convention so E=(+), W=(-)
mag_variation *= IIF(StrComp(strMagVarSign, "W"), 1, -1)
gps_mode = strMode
EndIf
Else
Move(gpsvals(1),3,NAN,1)
gps_mode = ""
EndIf
CallTable(tsdata)
CallTable(minutely)
NextScan
SlowSequence
Scan(5,min,1,0)
ntp_offset = NetworkTimeProtocol(NTP_SERVER,UTC_OFFSET*3600,MAX_CLOCK_DRIFT)
NextScan
EndProg