Better calculation of "scanraw" timeout
Ho-Ro opened this issue · comments
Ian,
My 1st heuristic approach was to pessimistic, in the meantime I did some timing checks and improved the calculations. Please mind that this was done on my tinySA3 a.k.a. tinySA Basic.
You should check with your tinySA4 a.k.a. tinySA Ultra, it can take an eternity at RBW = 0.2 KHz.
My checks:
- I set RBW to
auto
and then increased the frequency range while watching the RBW reading on the tinySA's display. I found a fairly linear dependence between frequency range and RBW of about 7 kHz per 1 MHz range. - The time for a scan is linearly dependent on the frequency range and quadratically dependent on the reciprocal of the RBW.
- Scan time is doubled when spur removal is activated.
- Other settings also may increase the scan time.
- The number of scan points hardly plays a role, it only occurs as USB transmission time, max. 1-2 ms per measured value.
- 20 MHz scan range with RBW = 3 kHz has a duration of about 83 seconds, so i chose the heuristic constant to
20e3
, this gives an overall timeout of 111 s. - As tinySA3 as well as tinySA4 transfer blocks of 20 values the timeout must be divided by the number of blocks, i.e.
points / 20
.
Have fun
Martin
def sweepTimeout(self, f_low, f_high): # freqs are in Hz
if self.rbw == 'auto':
# rbw auto setting from tinySA: ~7 kHz per 1 MHz scan frequency span
# TODO: check for tinySA Ultra
rbw = (f_high - f_low) * 7e-6
else:
rbw = float(self.rbw)
# lower / upper limit
if rbw < float(resBW[1]):
rbw = float(resBW[1])
elif rbw > float(resBW[-1]):
rbw = float(resBW[-1])
if self.rbw == 'auto':
logging.info(f'rbw (auto) = {rbw} kHz')
# timeout can be very long - use a heuristic approach
# 1st summand is the scanning time, 2nd summand is the USB transfer overhead
# TODO: times are for tinySA, check for tinySA Ultra if "20e3" is also correct
timeout = ((f_high - f_low) / 20e3) / (rbw ** 2) + self.points / 500
if self.spur: # scan time is doubled
timeout *= 2
# transfer is done in blocks of 20 points, this is the timeout for one block
self.timeout = timeout * 20 / self.points + 1 # minimum is 1 second
logging.info(f'sweepTimeout = {self.timeout} s')
Hi Martin,
Noted about scan points number having little impact. Using QtTinySA with my SA4, desktop PC, running from a terminal (3D disabled):
At 3kHz RBW 20MHz scan with 290 points takes around 25s for 80MHz fstart.
At 1240MHz start with SPUR=auto it takes 98s and with SPUR=off it takes 49s.
At 1kHz RBW 20MHz scan with 290 points takes around 170s for 80MHz fstart.
At 0.2kHz RBW with a 1MHz scan starting at 80MHz takes 185s
At 0.2kHz RBW with a 1MHz scan starting at 1240MHz with SPUR=auto it takes 370s and with SPUR=off it takes 187s.
Does the timeout value actually matter so long as it's big enough? Provided the expected number of bytes is received it's not invoked, is my understanding. Perhaps an alternative would be to set it high and error-trap a short reply?
I did some measurements yesterday of the serial transaction times which was interesting, using QElapsedTimer nsecsElapsed, around the 'dataBlock = (serialPort.read(3)) # read a block of 3 bytes of data'. I plotted some charts:
![30kHz_rbw_scantime_2](https://git
hub.com/g4ixt/QtTinySA/assets/76836635/1e13ee7b-e9ad-4f52-bc1a-9ced2f004801)
Every 20th read takes a long time - this is expected I guess because of the buffer size. But a lot of intermediate readings take more than 10x the time of the (majority) fastest. The 3 different graphs show the 20-block read times, then exclude those, then the long intermediate times, them the really fast ones. Do you know why this would be?
My original thought on serial port timeout was to set it initially using your algorithm on 'run' and then to measure the highest transaction time during 1 full scan, then to reduce the timeout to (say) 120% of the measured value.
By the way I tried building a Linux executable using pyinstaller and it came out at 600Mb. It won't run but gives a permissions error. I haven't investigated further but its permissions show as exectutable and me as owner.
Fun!
Ian
x axis on charts is 'index' number and y axis is time in mS
Thank you for your measurements, the Ultra is faster than the Basic - I should get a new toy for the wintertime :)
Does the timeout value actually matter so long as it's big enough? Provided the expected number of bytes is received it's not invoked, is my understanding.
Yes, but it should be as short as possible to detect a deadlock of the transfer.
Perhaps an alternative would be to set it high and error-trap a short reply?
No - normally an error means no response when expected to get data.
Every 20th read takes a long time - this is expected I guess because of the buffer size.
Exactly.
But a lot of intermediate readings take more than 10x the time of the (majority) fastest. The 3 different graphs show the 20-block read times, then exclude those, then the long intermediate times, them the really fast ones. Do you know why this would be?
Two reasons come to my mind, USB latences because it is a shared bus and also the threads of the tiny firmware are running on only one core, so the USB transfer may be delayed by another thread.
My original thought on serial port timeout was to set it initially using your algorithm on 'run' and then to measure the highest transbaction time during 1 full scan, then to reduce the timeout to (say) 120% of the measured value.
Interesting approach - but since Erik has assured that the scanraw
should be interruptible, I would not sacrifice too much time - better to wait for its correction. Then a hanging transmission can be stopped from your program.
included new timeout fn in code