-
Notifications
You must be signed in to change notification settings - Fork 51
Description
I was just going over the code a bit, have yet to try running anything. I saw in demod.py there's a straddled_packet flag that every work function it gets checked and updated. As best I can tell, this is pointless. The only usage of it (gr-adsb$ grep -rnw straddled_packet) is on line 63 to see if it's 1 and it sets it to 0 before doing anything. So functionally it seems to be serving no purpose? Should maybe remove it to reduce overhead / processing needs?
I've also been experimenting with some of the other functions as unit-tests. I sadly am struggling to get gnuradio of a modern version to install so I can't test the system, but:
anywhere there's an allocation of a zeros array and then setting values to 1 I think needs scrutiny. Specifically, stuff like
Lines 140 to 141 in 9d65482
| pulses = np.zeros(NUM_PREAMBLE_PULSES, dtype=int) | |
| pulses[amps > in0[pulse_idx]/2] = 1 |
Lines 83 to 84 in 9d65482
| in0_pulses = np.zeros(N+1, dtype=int) | |
| in0_pulses[np.insert(in0[0:N], 0, self.prev_in0) >= self.threshold] = 1 |
I believe you can just do
pulses = amps > in0[pulse_idx]/2and it'll take care of itself. In my testing it should also be fine to compare without conversions. The advantage to this is fewer operations, which leads to dramatically improved speed and simpler code.
import numpy as np
threshold = 0.010
data_in = np.random.rayleigh(size=100000000)
def func1():
data_in > threshold
def func2():
in0_pulses = np.zeros(100000000, dtype=int)
in0_pulses[data_in >= threshold] = 1
if __name__ == "__main__":
import pyperf
runner = pyperf.Runner()
runner.bench_func('func1', func1)
runner.bench_func('func2', func2)On my machine (modern AMD desktop), I get 29.9 +/- 1.2 ms vs 119 +/- 2ms, so around 4x faster, and it's less code.
I also saw https://github.com/mhostetter/gr-adsb/blob/9d654828ba70a4f4880e60357ffe977c68a58ad1/python/adsb/decoder.py#L366C9-L366C22. Replacing the function with
heading = (heading + 45/2) % 360
quad = int(heading / 45)
return ["E", "NE", "N", "NW", "W", "SW", "S", "SE"][quad]And testing as for h in np.linspace(-359, 359, 1000): ... got me 246 us +- 11 us vs 1.28 ms +- 0.02 ms. Dramatically less code, and again 5x faster. A check over that search space said my code's output matched the repo's. Changing the directions array to being defined elsewhere didn't show much if any improvement, so for locality I think it's good to just keep it there.
Lines 144 to 147 in 9d65482
| corr_matches = np.sum(pulses == self.preamble_pulses) | |
| # Only assert preamble found if all the 1/2 symbols match | |
| if corr_matches == NUM_PREAMBLE_PULSES: |
Looks like this could just be
if pulses == self.preamble_pulses
gr-adsb/python/adsb/decoder.py
Line 507 in 9d65482
| self.screen.refresh() |
refresh is being called every update, which is not how curses is designed. It should be called once at the end of the loop. I know it's not as easy to debug, but that's the preferred way.
Anywhere a string is created then immediately .format is called is slower than f strings. It's a bit minimal in the benchmarks (10% difference) but it's less code and every bit helps. So for example, "{:5.0f}".format(self.plane_dict[icao]["vertical_rate"]) becomes "f{self.plane_dict[icao]["vertical_rate"]:5.0f}"