StationName (TlkSnw) '' tlksnow.cr1 05mar2009 ki const SITE_ID = 7999 ' PakBus ID 15 const OUT_OF_SERVICE = 7999 ' example perl code to get the epoch seconds at start of year: ' $ perl -MTime::Local -e 'print timelocal(0,0,0,1,0,shift),"\n"' 2009 const UNIX_EPOCH_2009 = 1230800400 const TIMEZONE_OFFSET = 9*3600 ' TPS-3100 sensor is set to Alaska standard time const RADIO_ON_MIN = 0 const RADIO_OFF_MIN = 10 const RADIO_V_LIM = 12.2 const RADIO_HOLD_V_LIM = 12.6 '' control port allocations: const TSP_TX = 1 const TSP_RX = 2 const SR50e_CNTRL = 3 const SR50w_CNTRL = 5 const GNR_VW3_PWR = 6 const CS105_CNTRL = 7 ' barometer const HMP_PWR = 8 ' optional 12V relay const GEONOR_HTR = 8 ' conflicts with previous! '' sensor height is copied to vars sr50E_ht, etc. const SR50e_HT_CM = 161 const SR50w_HT_CM = 176.5 '' measured thermistor bridge resistor values, kOhms: const ATB1_REF_R = 15.03 const ATB2_REF_R = 14.99 const ATB3_REF_R = 15.04 '' geonor orifice heater RTD, Minco S665PFY40B, 1000 Ohms at 0C '' derived an empirical relation from dataset from -40 to 0C: const gnr_rtd_M = 0.25 ' degrees/Ohm const gnr_rtd_B = -250 ' degrees C const GNR_REF_R = 1.0 ' kOhms ' Steinhart & Hart coeff. for YSI 44004 thermistors, 2252 Ohms @ 25C: const a = 0.0014654354 const b = 0.0002386780 const c = 0.0000001000 ' Geonor T-200B2W vibrating wire xdcrs id & calibration const GNR_THRESHOLD = 0.1 ' mm const GNR_TIMECONSTANT = 60 ' seconds, digital filter time constant const GNR1_COLOR = "ORG" const GNR1_ORIENTATION = "S" const GNR1_SN = 31408 const GNR1_A = 1.67303e-002 const GNR1_B = 9.11597e-006 const GNR1_f0 = 1049.9 const GNR2_COLOR = "GRN" const GNR2_ORIENTATION = "NE" const GNR2_SN = 31308 const GNR2_A = 1.65629e-002 const GNR2_B = 9.17205e-006 const GNR2_f0 = 1044.7 const GNR3_COLOR = "YEL" const GNR3_ORIENTATION = "NW" const GNR3_SN = 31508 const GNR3_A = 1.66082e-002 const GNR3_B = 9.20175e-006 const GNR3_f0 = 1047.6 Dim time(9) ' realtime clock, aliased to thisMinute, thisSecond '' declare some utility variables which really should be local... Dim i ' ... generic loop variable Dim x ' ... sr50 filter time constant fraction dim fmin dim fmax '' first 7 public variables appear on first keypad screen: public sr50e_tc, sr50w_tc ' SR50 temp-corrected distance to surface, cm public AT, RH ' air temp & humidity from HMP45C public WS_mps, WD ' wind speed & direction from RMYoung public batt_volt public SolarPnlV, Line12V '' Geonor variables; prefix gnr: geonor vibrating wire public gnr_freq_raw(3) ' raw frequency measurement from transducer, Hz public gnr_freq(3) ' filtered frequency measurement public gnr_water(3) ' mm water currently represented in bucket public gnr_state(3) ' last water value after a significant change public gnr_precip(3) ' mm change from previous state, to 3 decimal places public gnr_vw3_enable as boolean public gnr_htr_enable as boolean public gnr_filter_tc public gnr_orifice_temp public gnr_rtd_R as float '' TPS-3100 variables public tps_msg as String * 64 dim tps_1970sec as long dim lgr_1970sec as long public tps_time_err public tps_fault ' 8-bit fault code in msg, convert to decimal value public tps_precip_rate ' mm/hr of water-equivalent precipitation public tps_precip_total ' total mm water sensed by system public tps_precip ' mm water change from previous scan public tps_AT public tps_WS public tps_PanelT dim precip_event as boolean units tps_precip = mm units tps_precip_rate = mm/hr units tps_AT = C units tps_WS = m/s units tps_PanelT = C '' measured snow depth sensor heights; see also constants above public sr50e_ht public sr50w_ht '' misc variables: public StationID public PanelT public ATB(3) ' GWS/YSI AT thermistors public DP ' dewpoint calculated from HMP45C AT & RH public tempcorrfactor ' for sr50 data processing public AtmPr ' barometric pressure, absolute '' west sr50 is over natural tundra, not trimmed public sr50w(2) ' sr50 distance (at 0C) & quality public sr50w_f1h, sr50w_f4h, sr50w_f12h ' filtered values public sr50w_fw1h, sr50w_fw4h, sr50w_fw12h ' weighted filtered values public sr50w_snwdpth ' calculated snow depth public sr50w_errors(3) '' east sr50 has tundra trimmed and bark placed below sensor public sr50e(2) public sr50e_f1h, sr50e_f4h, sr50e_f12h public sr50e_fw1h, sr50e_fw4h, sr50e_fw12h public sr50e_snwdpth public sr50e_errors(3) '' alternate sr50 3-echo mode for both sensors: public sr50w3(6), sr50w3tc(3) ' sr50 3 distances, 3 quality, 3 corrected dist. public sr50e3(6), sr50e3tc(3) '' radio control variables public RadioOnMin, RadioOffMin public RadioVlim, RadioHldVlim public Dim RadioHold as Boolean public Dim RadioForceOn as Boolean '' AT thermistors reference R, measured R: public ATB_Ref_R(3), ATB_R(3) '' the sr50_AT alias will be used for SR50 temperature '' corrections, so select an appropriate sensor here: alias AT = sr50_AT ' Alias ATB(1) = sr50_AT alias sr50e(1) = sr50e_raw ' raw distance from sr50e sensor alias sr50e(2) = sr50e_Q ' sensor quality metric alias sr50w(1) = sr50w_raw alias sr50w(2) = sr50w_Q alias time(5) = thisMinute alias time(6) = thisSecond alias DP = DewPt alias WD = WindDir alias WS_mps = WindSpeed alias sr50w3(1) = sr50w3_raw1 alias sr50w3(2) = sr50w3_raw2 alias sr50w3(3) = sr50w3_raw3 alias sr50w3(4) = sr50w3_Q1 alias sr50w3(5) = sr50w3_Q2 alias sr50w3(6) = sr50w3_Q3 alias sr50w3tc(1) = sr50w3_tc1 alias sr50w3tc(2) = sr50w3_tc2 alias sr50w3tc(3) = sr50w3_tc3 alias sr50w_errors(1) = sr50w_is0 alias sr50w_errors(2) = sr50w_isNaN alias sr50w_errors(3) = sr50w_Qis0 alias sr50e3(1) = sr50e3_raw1 alias sr50e3(2) = sr50e3_raw2 alias sr50e3(3) = sr50e3_raw3 alias sr50e3(4) = sr50e3_Q1 alias sr50e3(5) = sr50e3_Q2 alias sr50e3(6) = sr50e3_Q3 alias sr50e3tc(1) = sr50e3_tc1 alias sr50e3tc(2) = sr50e3_tc2 alias sr50e3tc(3) = sr50e3_tc3 alias sr50e_errors(1) = sr50e_is0 alias sr50e_errors(2) = sr50e_isNaN alias sr50e_errors(3) = sr50e_Qis0 units AT = C units sr50_AT = C units RH = % units ATB(1) = C units ATB(2) = C units ATB(3) = C units ATB_R(1) = kOhms units ATB_R(2) = kOhms units ATB_R(3) = kOhms units ATB_Ref_R(1) = kOhms units ATB_Ref_R(2) = kOhms units ATB_Ref_R(3) = kOhms units AtmPr = mb units WS_mps = m/s units WD = deg units SR50e_snwdpth = cm units SR50w_snwdpth = cm units DP = C units sr50w_raw = cm units sr50e_raw = cm units sr50w3_raw1 = cm units sr50w3_raw2 = cm units sr50w3_raw3 = cm units sr50e3_raw1 = cm units sr50e3_raw2 = cm units sr50e3_raw3 = cm '''''''''''''''''''''''''''''' '' daily tables: '''''''''''''''''''''''''''''' DataTable (Daily,1,-1) DataInterval(0,86400,sec,1) Average (1,AtmPr,fp2,0) Average (1,AT,fp2,0) Average (3,ATB(),fp2,0) Average (1,tps_AT,fp2,0) Average (1,RH,fp2,0) WindVector (1,WS_mps,WD,fp2,0,0,0,0) Average (1,tps_WS,fp2,0) Average (1,sr50w_snwdpth,fp2,0) Average (1,sr50e_snwdpth,fp2,0) Sample (1,sr50w_fw12h,fp2) Sample (1,sr50e_fw12h,fp2) Totalize (1,tps_precip,fp2,0) Totalize (3,gnr_precip,fp2,0) EndTable DataTable (DailyRaw,1,-1) DataInterval(0,86400,sec,0 Average (3,ATB_R(),fp2,0) Minimum (3,ATB_R(),fp2,0,0) Maximum (3,ATB_R(),fp2,0,0) EndTable '''''''''''''''''''''''''''''' '' hourly tables: '''''''''''''''''''''''''''''' DataTable (AtmsSmp,1,-1) DataInterval (0,3600,sec,1) Sample (1,AtmPr,fp2) Sample (1,AT,fp2) Sample (3,ATB,fp2) Sample (1,tps_AT,fp2) Sample (1,RH,fp2) Sample (1,DP,fp2) Sample (1,WS_mps,fp2) Sample (1,WD,fp2) Sample (1,tps_WS,fp2) Sample (1,sr50w_snwdpth,fp2) Sample (1,sr50e_snwdpth,fp2) Sample(1,tps_precip_total,fp2) Sample(3,gnr_state,fp2) EndTable DataTable (AtmsAvg,1,-1) DataInterval (0,3600,sec,1) Average (1,AtmPr,fp2,0) Average (1,AT,fp2,0) Average (3,ATB,fp2,0) Average (1,tps_AT,fp2,0) Average (1,RH,fp2,0) Average (1,DP,fp2,0) WindVector (1,WS_mps,WD,fp2,0,0,0,0) Average (1,tps_WS,fp2,0) Average (1,sr50w_snwdpth,fp2,0) Average (1,sr50e_snwdpth,fp2,0) Sample (1,sr50w_fw1h,fp2) Sample (1,sr50e_fw1h,fp2) Totalize (1,tps_precip,fp2,0) Totalize (3,gnr_precip,fp2,0) EndTable DataTable (AtmsMax,1,-1) DataInterval (0,3600,sec,1) Minimum (1,AtmPr,fp2,0,0) Maximum (1,AtmPr,fp2,0,0) Minimum (1,AT,fp2,0,0) Maximum (1,AT,fp2,0,0) Minimum (1,ATB,fp2,0,0) Maximum (1,ATB,fp2,0,0) Minimum (1,tps_AT,fp2,0,0) Maximum (1,tps_AT,fp2,0,0) Minimum (1,RH,fp2,0,0) Maximum (1,RH,fp2,0,0) Minimum (1,DP,fp2,0,0) Maximum (1,DP,fp2,0,0) Minimum (1,WS_mps,fp2,0,0) Maximum (1,WS_mps,fp2,0,0) Minimum (1,tps_WS,fp2,0,0) Maximum (1,tps_WS,fp2,0,0) Minimum (1,sr50w_snwdpth,fp2,0,0) Maximum (1,sr50w_snwdpth,fp2,0,0) Minimum (1,sr50e_snwdpth,fp2,0,0) Maximum (1,sr50e_snwdpth,fp2,0,0) Minimum(3,gnr_state,fp2,0,0) Maximum(3,gnr_state,fp2,0,0) EndTable DataTable (HrlyDiag,1,-1) DataInterval (0,3600,sec,1) Average (1,batt_volt,fp2,0) Average (1,SolarPnlV,fp2,0) Average (1,Line12V,fp2,0) Sample (1,PanelT,fp2) Average(1, tps_PanelT, fp2,0) EndTable DataTable (HrlyRaw,1,-1) DataInterval (0,3600,sec,1) Average (3,ATB_R(),fp2,0) Average (1,gnr_rtd_r,float,0) Minimum (1,gnr_rtd_r,float,0,0) Maximum (1,gnr_rtd_r,float,0,0) EndTable DataTable (SnwDpth,1,-1) DataInterval (0,3600,sec,1) Average (1,SR50w_snwdpth,fp2,0) Sample(1,sr50w_ht,fp2) Average(1,sr50w_tc,fp2,0) Average (1,SR50e_snwdpth,fp2,0) Sample(1,sr50e_ht,fp2) Average(1,sr50e_tc,fp2,0) EndTable DataTable (SR50W,1,-1) DataInterval (0,3600,sec,1) Average(1,sr50_AT,fp2,0) Average(1,sr50w_raw,fp2,0) Average(1,sr50w_tc,fp2,0) Sample(1,sr50w_f1h,fp2) Sample(1,sr50w_f4h,fp2) Sample(1,sr50w_f12h,fp2) Totalize(3, sr50w_errors(),fp2,0) Minimum (1,sr50w_Q,fp2,0,0) Average(1,sr50w_Q,fp2,0) Maximum (1,sr50w_Q,fp2,0,0) EndTable DataTable (SR50E,1,-1) DataInterval (0,3600,sec,1) Average(1,sr50_AT,fp2,0) Average(1,sr50e_raw,fp2,0) Average(1,sr50e_tc,fp2,0) Sample(1,sr50e_f1h,fp2) Sample(1,sr50e_f4h,fp2) Sample(1,sr50e_f12h,fp2) Totalize(3, sr50e_errors(),fp2,0) Minimum (1,sr50e_Q,fp2,0,0) Average(1,sr50e_Q,fp2,0) Maximum (1,sr50e_Q,fp2,0,0) EndTable DataTable (PrecipHr, 1, -1) DataInterval (0,3600,sec,1) Average(1, tps_precip_rate,fp2,0) Sample(1, tps_precip_total,fp2) Sample(3,gnr_state,fp2) Average(3,gnr_freq,fp2,0) Average(1, tps_AT, fp2,0) Average(1, tps_WS, fp2,0) EndTable '''''''''''''''''''''''''''''''''''''''''' '' fixed-size tables, wrap after 5 days: '''''''''''''''''''''''''''''''''''''''''' '' table size calculated as: days*minutes_per_day/interval_minutes DataTable (SR50W3, 1, 5*24*60/5) DataInterval (0,300,Sec,1) Sample(1,sr50_AT,fp2) Sample(6,sr50w3,fp2) Sample(3,sr50w3tc,fp2) EndTable DataTable (SR50E3, 1, 5*24*60/5) DataInterval (0,300,Sec,1) Sample(1,sr50_AT,fp2) Sample(6,sr50e3,fp2) Sample(3,sr50e3tc,fp2) EndTable DataTable (Precip5m, 1, 5*24*60/5) DataInterval (0,300,Sec,1) Sample(1, tps_time_err, long) Sample(1, tps_fault, fp2) Sample(1, tps_precip_rate,fp2) Sample(1, tps_precip_total,fp2) Sample(1, tps_AT, fp2) Sample(1, tps_PanelT, fp2) Sample(1, tps_WS, fp2) Average(3,gnr_state,fp2,0) EndTable DataTable (Geonor5m, 1, 5*24*60/5) DataInterval (0,300,Sec,1) Sample(3,gnr_freq,fp2) Average(3,gnr_freq,fp2,0) Minimum(3,gnr_freq,fp2,0,0) Maximum(3,gnr_freq,fp2,0,0) Sample(1,gnr_filter_tc,fp2) Average(1,gnr_filter_tc,fp2,0) Average(3,gnr_freq_raw,fp2,0) Minimum(3,gnr_freq_raw,fp2,0,0) Maximum(3,gnr_freq_raw,fp2,0,0) Sample (1,gnr_rtd_r,float) Sample (1,gnr_orifice_temp,fp2) EndTable '''''''''''''''''''''''''''''''''''''''''' '' event-driven tables '''''''''''''''''''''''''''''''''''''''''' DataTable (PrecipEv, precip_event, 2880) Sample (3,gnr_precip,fp2) Sample (1,tps_precip,fp2) Sample (1,tps_precip_rate,fp2) Sample (1,tps_WS,fp2) EndTable DataTable (TPSfault,tps_fault,2880) Sample (1,tps_fault,fp2) Sample (1,tps_msg,string) EndTable '''''''''''''''''''''''''''''''''''''''''' '' subroutines '''''''''''''''''''''''''''''''''''''''''' Sub Read_HMP45AC VoltSE (AT,1,mV2500,6,1,0,250,0.1,-40.0) VoltSE (RH,1,mV2500,5,1,0,250,0.1,0) DewPoint (DP,AT,RH) EndSub ' Read_HMP45AC Sub Read_Thermistors Dim lnR Dim ATB_X(3) ' bridge resistance ratio '' read and process 3 AT thermistors wired to SE14,15,16: BrHalf (ATB_X(),3,mV2500,14,Vx2,3,2500,0,0,250,1.0,0) For i=1 To 3 ATB_R(i) = ATB_Ref_R(i)*ATB_X(i)/(1-ATB_X(i)) lnR = LN (1000*ATB_R(i)) ATB(i) = (1/(a + b*lnR + c*lnR^3)) - 273.15 Next i EndSub ' Read_Thermistors Sub Read_Geonor_RTD Dim gnr_x '' read and process RTD on Geonor precip gauge orifice, near heater BrHalf (gnr_x,1,mV250,12,Vx1,1,250,0,0,250,1.0,0) gnr_rtd_R = GNR_REF_R*gnr_x/(1-gnr_x) gnr_orifice_temp = gnr_rtd_R * 1000.0 * gnr_rtd_M + gnr_rtd_B EndSub ' Read_Geonor_RTD Sub Read_SR50_East '' east sr50 sensor is over trimmed tundra with bark '' sr50 M1 command returns distance [cm] and quality number SDI12Recorder(sr50e,SR50E_CNTRL,0,"M1!",100.0,0) '' remove above multiplier from quality value sr50e_Q = sr50e_Q / 100.0 '' temperature-corrected distance, negative value: sr50e_tc = -sr50e_raw * tempcorrfactor '' offset by assumed sensor height for resultant snowdepth [cm]: sr50e_snwdpth = sr50e_ht + sr50e_tc '' reset error indicators: sr50e_is0 = 0 sr50e_isNaN = 0 sr50e_Qis0 = 0 '' update filter values if all is ok: if (sr50e_raw <> 0) AND (sr50e_Q <> 0) AND (sr50e_raw <> NaN) then '' filter fraction is sensorScan / timeConstant '' sensorScan is set in the main loop to 60 seconds '' timeConstant is 1, 4, and 12 hours (in seconds) x = 60/3600 '' 1 hour time constant sr50e_f1h = sr50e_tc * x + sr50e_f1h * (1 - x) x = x / 4 '' 4 hour time constant sr50e_f4h = sr50e_tc * x + sr50e_f4h * (1 - x) x = x / 3 '' 12 hour time constant sr50e_f12h = sr50e_tc * x + sr50e_f12h * (1 - x) '' repeat filters with quality and wind weighting factors '' applied to the time constants: '' quality factor to diminish impact when quality is poor; '' arbitrary function used: 1=best, lower=poorer x = 162 ^ 2 / sr50e_Q ^ 2 '' wind factor to diminish impact when wind is high... '' wind factor not used, so set to 1 x = x * 1 '' start with weighted 1 hour time constant: x = x * 60 / 3600 '' 1 hour time constant sr50e_fw1h = sr50e_tc * x + sr50e_fw1h * (1 - x) x = x / 4 '' weighted 4 hour time constant sr50e_fw4h = sr50e_tc * x + sr50e_fw4h * (1 - x) x = x / 3 '' weighted 12 hour time constant sr50e_fw12h = sr50e_tc * x + sr50e_fw12h * (1 - x) else if sr50e_raw = 0 then sr50e_is0 = 1 if sr50e_raw = NaN then sr50e_isNaN = 1 if sr50e_Q = 0 then sr50e_Qis0 = 1 endif EndSub ' Read_SR50_East Sub Read_SR50_West '' west sr50 sensor is over natural, undisturbed tundra SDI12Recorder(sr50w,SR50W_CNTRL,0,"M1!",100.0,0) sr50w_Q = sr50w_Q / 100.0 sr50w_tc = -sr50w_raw * tempcorrfactor sr50w_snwdpth = sr50w_ht + sr50w_tc sr50w_is0 = 0 sr50w_isNaN = 0 sr50w_Qis0 = 0 if (sr50w_raw <> 0) AND (sr50w_Q <> 0) AND (sr50w_raw <> NaN) then x = 60 / 3600 '' 1 hour time constant sr50w_f1h = sr50w_tc * x + sr50w_f1h * (1 - x) x = x / 4 '' 4 hour time constant sr50w_f4h = sr50w_tc * x + sr50w_f4h * (1 - x) x = x / 3 '' 12 hour time constant sr50w_f12h = sr50w_tc * x + sr50w_f12h * (1 - x) x = 162 ^ 2 / sr50w_Q ^ 2 x = x * 1 x = x * 60 / 3600 '' 1 hour time constant sr50w_fw1h = sr50w_tc * x + sr50w_fw1h * (1 - x) x = x / 4 '' weighted 4 hour time constant sr50w_fw4h = sr50w_tc * x + sr50w_fw4h * (1 - x) x = x / 3 '' weighted 12 hour time constant sr50w_fw12h = sr50w_tc * x + sr50w_fw12h * (1 - x) else if sr50w_raw = 0 then sr50w_is0 = 1 if sr50w_raw = NaN then sr50w_isNaN = 1 if sr50w_Q = 0 then sr50w_Qis0 = 1 endif EndSub ' Read_SR50_West Sub RadioControl ( onMin, offMin, batV, Vlimit, holdVlimit ) '' turn SW12() on & off at specified minutes into interval '' battery voltage must be above Vlimit to turn radio on '' global RadioHold flag prevents turning radio OFF if set '' RadioHold flag is cleared if battery < holdVlimit '' special case: radio turns on at noon regardless of V limits '' subroutine should normally run every minute If RadioForceOn Then SW12(1) If thisMinute = onMin Then If batV >= Vlimit then SW12(1) if RadioHold AND batV < holdVlimit Then RadioHold = 0 Endif If IfTime(12, 24, hr) Then SW12(1) If thisMinute = offMin Then If NOT RadioHold Then SW12(0) Endif EndSub ' RadioControl Sub Run_at_Startup '' turn radio on and read settings from constants: SW12(1) ' turn radio on! RadioOnMin = RADIO_ON_MIN RadioOffMin = RADIO_OFF_MIN RadioVlim = RADIO_V_LIM RadioHldVlim = RADIO_HOLD_V_LIM RadioHold = 1 '' measured values of ATB reference resistors: ATB_Ref_R(1) = ATB1_REF_R ATB_Ref_R(2) = ATB2_REF_R ATB_Ref_R(3) = ATB3_REF_R '' copy initial sensor heights into variables: sr50w_ht = SR50W_HT_CM sr50e_ht = SR50E_HT_CM '' seed west sr50 filter values with initial reading: sr50w_f1h = sr50w_tc sr50w_f4h = sr50w_tc sr50w_f12h = sr50w_tc sr50w_fw1h = sr50w_tc sr50w_fw4h = sr50w_tc sr50w_fw12h = sr50w_tc '' seed east sr50 filter values with initial reading: sr50e_f1h = sr50e_tc sr50e_f4h = sr50e_tc sr50e_f12h = sr50e_tc sr50e_fw1h = sr50e_tc sr50e_fw4h = sr50e_tc sr50e_fw12h = sr50e_tc '' turn on the power relay for this transducer: gnr_vw3_enable = True gnr_htr_enable = False EndSub ' Run_at_Startup Sub read_Geonor dim f_minus_f0 '' every minute, read and update filter for sensor frequency PeriodAvg(gnr_freq_raw(1),1,mV250,9,0,1,1000,1000,1,0) PeriodAvg(gnr_freq_raw(2),1,mV250,10,0,1,2000,1000,1,0) if gnr_vw3_enable then PeriodAvg(gnr_freq_raw(3),1,mV250,11,0,1,2000,1000,1,0) else gnr_freq(3) = 0 endif '' tc weighting factor: 1 when WS=0, 5 when WS=15 (arbitrary relation) gnr_filter_tc = GNR_TIMECONSTANT * (tps_ws * 4 / 15 + 1) x = 60/gnr_filter_tc for i = 1 to 3 if gnr_freq(i) = 0 or gnr_freq(i) = NaN then gnr_freq(i) = gnr_freq_raw(i) ' seed or init value endif gnr_freq(i) = gnr_freq_raw(i) * x + gnr_freq(i) * (1 - x) next i '' produce for each xdcr: frequency, mm water in bucket, mm change this scan '' clear precip mm event variables: for i = 1 to 3 gnr_precip(i) = 0 next i '' for each sensor, run frequency to mm water conversion '' using this relation with calibration constants: '' P = A(f-f0) + B(f-f0)^2 f_minus_f0 = gnr_freq(1) - GNR1_f0 ' freq diff from empty bucket gnr_water(1) = ( GNR1_A + GNR1_B * f_minus_f0 ) * f_minus_f0 * 10 ' mm h2o if abs(gnr_water(1) - gnr_state(1)) >= GNR_THRESHOLD then gnr_precip(1) = gnr_water(1) - gnr_state(1) gnr_state(1) = gnr_water(1) ' store as new state for mm water precip_event = true endif f_minus_f0 = gnr_freq(2) - GNR2_f0 gnr_water(2) = ( GNR2_A + GNR2_B * f_minus_f0 ) * f_minus_f0 * 10 if abs(gnr_water(2) - gnr_state(2)) >= GNR_THRESHOLD then gnr_precip(2) = gnr_water(2) - gnr_state(2) gnr_state(2) = gnr_water(2) precip_event = true endif ' transducer vw3 has a power relay, so skip if not turned on: if GNR_VW3_PWR then f_minus_f0 = gnr_freq(3) - GNR3_f0 gnr_water(3) = ( GNR3_A + GNR3_B * f_minus_f0 ) * f_minus_f0 * 10 if abs(gnr_water(3) - gnr_state(3)) >= GNR_THRESHOLD then gnr_precip(3) = gnr_water(3) - gnr_state(3) gnr_state(3) = gnr_water(3) precip_event = true endif endif EndSub ' Read_Geonor Sub Read_TPS3100 dim p dim t as long dim tps_rawdata as String * 54 dim tps_chksum as String * 4 SerialOpen (Com1,9600,0,300,10000) SerialFlush (Com1) SerialOut (Com1, chr(13) & chr(13) & "T" & chr(13),"",3,0) RealTime(time()) SerialIn (tps_msg,Com1,100,-1,100) SerialClose (Com1) '' result message includes command (T) and several fixed-width fields: '' T 001,1233694757,0000000,00.00,00009.72,-40.9,-29.4,01.7*FCF3 '' 3 7 18 26 32 41 47 53 58 tps_fault = 0 tps_rawdata = mid(tps_msg,3,54) tps_chksum = mid(tps_msg,58,4) if checksum(tps_rawdata,10,0) <> hextodec(tps_chksum) then tps_fault = 1024 ' checksum error else ' checksum was good! '' check time: subtract tps time (unix epoch) from logger time lgr_1970sec = UNIX_EPOCH_2009 + time(9)*86400 + (time(4)*60 + time(5))*60 + time(6) tps_1970sec = mid(tps_msg,7,10) tps_time_err = lgr_1970sec - tps_1970sec - TIMEZONE_OFFSET if strcomp( mid(tps_msg,3,3), "001") <> 0 then tps_fault = 1 ' will shift to 2^9, or 512 endif for i = 18 to 24 ' shift fault bits 1 to 8 into decimal fault value, p = mid(tps_msg,i,1) tps_fault = tps_fault * 2 + p next i '' parse ancilliary data, air and instrument temperature, wind speed tps_AT = mid(tps_msg,41,5) tps_PanelT = mid(tps_msg,47,5) tps_WS = mid(tps_msg,53,4) tps_precip_rate = mid(tps_msg,26,5) '' will log event when non-0 p = mid(tps_msg,32,8) if p = tps_precip_total then '' no change, so zero-out event variable tps_precip = 0 else '' log when total changes tps_precip = p - tps_precip_total tps_precip_total = p precip_event = true endif endif EndSub ' Read_TPS3100 BeginProg StationID = SITE_ID '' at startup: '' copy config constants into variables '' turn radio and RadioHold on when the program starts! '' seed sr50 filters with starting value call Run_at_Startup Scan (60,Sec,0,0) precip_event = false Battery (batt_volt) PortSet (GNR_VW3_PWR, gnr_vw3_enable) PortSet (CS105_CNTRL, 1) ' enable barometer RealTime(time()) ' sets thisMinute variable PanelTemp (PanelT,250) VoltSE (SolarPnlV,1,mV2500,7,1,0,250,0.011,0) VoltSE (Line12V,1,mV2500,8,1,0,250,0.011,0) '' read HMP sensor '' HMP relay control port is also Geonor orifice heater, on all the time PortSet (HMP_PWR, 1) Delay (0,150,mSec) ' allow sensor to settle... call Read_HMP45AC PortSet (HMP_PWR, 0) PortSet (GEONOR_HTR, gnr_htr_enable) '' read AT thermistors and Geonor orifice RTD: call Read_Thermistors call Read_Geonor_RTD '' read wind speed & direction PulseCount (WS_mps,1,1 ,1,1,.098,0) brhalf(WD,1,mV2500,13,Vx3,1,2500,true,200,250,355,0) '' read tps sensors: call Read_TPS3100 call RadioControl (RadioOnMin, RadioOffMin, batt_volt, RadioVlim, RadioHldVlim ) '' read sr50 sensors, process variables, filters... '' calculate temperature correction factor: tempcorrfactor = SQR((sr50_AT+273.15)/273.15) call Read_SR50_West call Read_SR50_East If thisMinute mod 5 = 0 Then '' read each SR50 with 3-echo mode, no multiplier or offset: SDI12Recorder(sr50W3,SR50W_CNTRL,0,"M3!",1,0) SDI12Recorder(sr50E3,SR50E_CNTRL,0,"M3!",1,0) '' apply tempcorrfactor and convert distances to negative cm: for i=1 To 3 '' correct distances as f(AT) and convert from +m to -cm sr50w3tc(i) = sr50w3(i) * tempcorrfactor * -100.0 sr50e3tc(i) = sr50e3(i) * tempcorrfactor * -100.0 next i Endif '' read barometer last, then turn it off '' note that barometer should be on at least 1 sec to read VoltSE (AtmPr,1,mV5000,4,1,0,250,0.184,600) PortSet (CS105_CNTRL, 0) '' call each table on every scan, let it figure out what to do '' hourly: calltable AtmsSmp calltable AtmsAvg calltable AtmsMax calltable HrlyDiag calltable HrlyRaw calltable SnwDpth calltable SR50W calltable SR50E calltable PrecipHr '' 5-day: calltable SR50W3 calltable SR50E3 calltable Precip5m '' event: calltable TPSfault '' daily: calltable Daily calltable DailyRaw '' run & log experimental high-speed sampling of geonor xdcrs: call read_Geonor calltable Geonor5m calltable PrecipEv NextScan EndProg