@@ -46,7 +46,10 @@ def __init__(self, portName: Optional[str] = None):
4646 # Normally we just sleep with a timeout on this condition (polling the power measurement data repeatedly)
4747 # but any time our measurements have been fully consumed (via reset_measurements) we notify() this condition
4848 # to trigger a new reading ASAP.
49- self .want_measurement = threading .Condition ()
49+ self ._want_measurement = threading .Condition ()
50+
51+ # To guard against a brief window while updating measured values
52+ self ._result_lock = threading .Condition ()
5053
5154 self .r = r = ppk2_api .PPK2_API (
5255 portName
@@ -62,8 +65,10 @@ def __init__(self, portName: Optional[str] = None):
6265 def measurement_loop (self ):
6366 """Endless measurement loop will run in a thread."""
6467 while self .measuring :
65- with self .want_measurement :
66- self .want_measurement .wait (0.0001 if self .num_data_reads == 0 else 0.001 )
68+ with self ._want_measurement :
69+ self ._want_measurement .wait (
70+ 0.0001 if self .num_data_reads == 0 else 0.001
71+ )
6772 # normally we poll using this timeout, but sometimes
6873 # reset_measurement() will notify us to read immediately
6974
@@ -75,17 +80,20 @@ def measurement_loop(self):
7580
7681 # update invariants
7782 if len (samples ) > 0 :
78- if (
79- self .current_num_samples == 0
80- ): # First set of new reads, reset min/max
83+ if self .current_num_samples == 0 :
84+ # First set of new reads, reset min/max
8185 self .current_max = 0
82- self .current_min = samples [
83- 0
84- ] # we need at least one sample to get an initial min
86+ self .current_min = samples [0 ]
87+ # we need at least one sample to get an initial min
88+
89+ # The following operations could be expensive, so do outside of the lock
90+ # FIXME - change all these lists into numpy arrays to use lots less CPU
8591 self .current_max = max (self .current_max , max (samples ))
8692 self .current_min = min (self .current_min , min (samples ))
87- self .current_sum += sum (samples )
88- self .current_num_samples += len (samples )
93+ latest_sum = sum (samples )
94+ with self ._result_lock :
95+ self .current_sum += latest_sum
96+ self .current_num_samples += len (samples )
8997 # logging.debug(f"PPK2 data_len={len(read_data)}, sample_len={len(samples)}")
9098
9199 self .num_data_reads += 1
@@ -102,13 +110,14 @@ def get_max_current_mA(self):
102110
103111 def get_average_current_mA (self ):
104112 """Return the average current in mA."""
105- if self .current_num_samples != 0 :
106- # If we have new samples, calculate a new average
107- self .current_average = self .current_sum / self .current_num_samples
113+ with self ._result_lock :
114+ if self .current_num_samples != 0 :
115+ # If we have new samples, calculate a new average
116+ self .current_average = self .current_sum / self .current_num_samples
108117
109- # Even if we don't have new samples, return the last calculated average
110- # measurements are in microamperes, divide by 1000
111- return self .current_average / 1000
118+ # Even if we don't have new samples, return the last calculated average
119+ # measurements are in microamperes, divide by 1000
120+ return self .current_average / 1000
112121
113122 def reset_measurements (self ):
114123 """Reset current measurements."""
@@ -122,8 +131,9 @@ def reset_measurements(self):
122131 self .num_data_reads = 0
123132 self .total_data_len = 0
124133 self .max_data_len = 0
125- with self .want_measurement :
126- self .want_measurement .notify () # notify the measurement loop to read immediately
134+
135+ with self ._want_measurement :
136+ self ._want_measurement .notify () # notify the measurement loop to read immediately
127137
128138 def close (self ) -> None :
129139 """Close the power meter."""
0 commit comments