Add logic to end the speed test if the result is stable

This commit is contained in:
2024-02-20 13:32:38 +01:00
parent c635cc4a8b
commit 8c0e38349a
2 changed files with 68 additions and 22 deletions

View File

@@ -18,6 +18,17 @@ import Speedtest from "./Speedtest.svelte";
servers to your computer. This speed is not affected by the daily servers to your computer. This speed is not affected by the daily
download limit for free users. download limit for free users.
</p> </p>
<p>
The speed measured is the maximum speed reached for two continuous
seconds during the test. The normal test has a time limit of 30
seconds and the long test 60 seconds. When the maximum speed has not
changed for a third of the test duration (10 or 20 seconds) the test
will end and the result will be final.
</p>
<p>
The speed gauge uses a logarithmic scale, which means that each
datapoint is 10 times more than the last one.
</p>
<h2>What do the numbers mean?</h2> <h2>What do the numbers mean?</h2>
<p> <p>
@@ -46,10 +57,7 @@ import Speedtest from "./Speedtest.svelte";
</p> </p>
<p> <p>
The last number shows how much data the speedtest was able to The last number shows how much data the speedtest was able to
transfer in the duration of the test. The standard test is six transfer in the duration of the test.
seconds and the long test is twelve seconds. The long test might be
slightly more accurate than the short one, but it uses more
bandwidth.
</p> </p>
<h2>Why is the speed different from other speed tests?</h2> <h2>Why is the speed different from other speed tests?</h2>

View File

@@ -33,7 +33,7 @@ const start = async (dur = 6000) => {
const reader = req.body.getReader(); const reader = req.body.getReader();
measure_speed(reader, update_interval, test_duration) measure_speed(() => reader.cancel(), test_duration)
while(true) { while(true) {
const {done, value} = await reader.read() const {done, value} = await reader.read()
@@ -49,17 +49,30 @@ const start = async (dur = 6000) => {
// Average speed for the whole test // Average speed for the whole test
let speed = 0 let speed = 0
let result_link = "" let result_link = ""
let current_duration = 0
const measure_speed = (reader, update_interval, test_duration) => { let progress_duration = 0
let progress_unchanged = 0
const measure_speed = (stop, test_duration) => {
speed = 0 speed = 0
result_link = "" result_link = ""
// This slice contains the speed measurements for 1/3 the duration of the // Updates per second
// test. This value is averaged and if the average is higher than the const ups = (1000/update_interval)
// previously calculated average then it is saved
const hist = new Uint32Array((test_duration/3)/update_interval) // This slice contains the speed measurements for four seconds of the test.
// This value is averaged and if the average is higher than the previously
// calculated average then it is saved. The resulting speed is the highest
// speed that was sustained for four seconds at any point in the test
const hist = new Uint32Array(ups*2)
let idx = 0 let idx = 0
// This var measures for how many ticks the max speed has not changed. When
// the speed has not changed for a third of the test duration the test is
// considered over
let unchanged = 0
const unchanged_limit = (test_duration/3)/update_interval
let previous_transferred = 0 let previous_transferred = 0
const start = Date.now() const start = Date.now()
@@ -72,26 +85,44 @@ const measure_speed = (reader, update_interval, test_duration) => {
idx++ idx++
// Calculate the average of all the speed measurements // Calculate the average of all the speed measurements
const sum = hist.reduce((acc, val) => acc + val, 0) const sum = hist.reduce((acc, val) => {
const new_speed = (sum/hist.length)*(1000/update_interval) if (val !== 0) {
acc.sum += val
acc.count++
}
return acc
}, {sum: 0, count: 0})
const new_speed = (sum.sum/sum.count)*ups
if (new_speed > speed) { if (new_speed > speed) {
speed = new_speed speed = new_speed
unchanged = 0
} else {
unchanged++
} }
// Only used for the progress bar // Update the duration of the test. Used for calculating progress and
current_duration = Date.now() - start // clock drift
const current_duration = Date.now() - start
if (idx < test_duration/update_interval) { // Update the progress bar
progress_unchanged = unchanged/unchanged_limit
progress_duration = current_duration/test_duration
if (idx < test_duration/update_interval && unchanged < unchanged_limit) {
// We have to manually calculate and subtract drift, because in my // We have to manually calculate and subtract drift, because in my
// tests with setInterval the clock would drift like 200ms in a // tests with setInterval the clock would drift like 200ms in a
// single test which significantly impacts results // single test which significantly impacts results
setTimeout(measure, update_interval - (current_duration-(idx*update_interval))) setTimeout(measure, update_interval - (current_duration-(idx*update_interval)))
} else { } else {
// Test is done, break the reader out of the counting loop // Test is done, break the reader out of the counting loop
await reader.cancel() await stop()
console.debug("Done! Test ran for", current_duration) console.debug(
current_duration = 0 "Done! Test ran for", current_duration,
"result did not change for", unchanged*update_interval,
)
progress_unchanged = 0
progress_duration = 0
// Update the URL so the results can be shared // Update the URL so the results can be shared
history.replaceState( history.replaceState(
@@ -132,8 +163,8 @@ onMount(() => {
<section class="highlight_border"> <section class="highlight_border">
<div style="text-align: center"> <div style="text-align: center">
<Button icon="speed" label="Start test" click={() => start(6000)} disabled={running} highlight={!running}/> <Button icon="speed" label="Start test" click={() => start(30000)} disabled={running} highlight={!running}/>
<Button icon="speed" label="Long test" click={() => start(12000)} disabled={running}/> <Button icon="speed" label="Long test" click={() => start(60000)} disabled={running}/>
<Button <Button
highlight_on_click highlight_on_click
disabled={result_link === ""} disabled={result_link === ""}
@@ -143,7 +174,14 @@ onMount(() => {
/> />
</div> </div>
<ProgressBar animation="linear" speed={update_interval} total={test_duration} used={current_duration}/> <!-- This progress bar shows either the progress for the test duration, or
when the test will time out. Whichever is higher -->
<ProgressBar
animation="linear"
speed={update_interval}
used={Math.max(progress_unchanged, progress_duration)}
total={1}
/>
<div class="speed_stats"> <div class="speed_stats">
<div class="highlight_shaded">{formatDataVolume(speed, 4)}/s</div> <div class="highlight_shaded">{formatDataVolume(speed, 4)}/s</div>