Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support human-readable and auto-scaled rate units for output #194

Merged
merged 1 commit into from
May 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,27 @@ Flags:
--help Show context-sensitive help (also try --help-long and --help-man).
-l, --list Show available speedtest.net servers.
-s, --server=SERVER ... Select server id to speedtest.
--custom-url=CUSTOM-URL Specify the url of the server instead of getting a list from speedtest.net.
--custom-url=CUSTOM-URL Specify the url of the server instead of fetching from speedtest.net.
--saving-mode Test with few resources, though low accuracy (especially > 30Mbps).
--json Output results in json format.
--location=LOCATION Change the location with a precise coordinate.
--location=LOCATION Change the location with a precise coordinate (format: lat,lon).
--city=CITY Change the location with a predefined city label.
--city-list List all predefined city labels.
--proxy=PROXY Set a proxy(http[s] or socks) for the speedtest.
eg: --proxy=socks://10.20.0.101:7890
eg: --proxy=http://10.20.0.101:7890
--source=SOURCE Bind a source interface for the speedtest.
--dns-bind-source DNS request binding source.(Experimental)
--dns-bind-source DNS request binding source (experimental).
eg: --source=10.20.0.101
-m --multi Enable multi-server mode.
-t --thread=THREAD Set the number of concurrent connections.
--search=SEARCH Fuzzy search servers by a keyword.
--ua Set the user-agent header for the speedtest.
--no-download Disable download test.
--no-upload Disable upload test.
--ping-mode Select a method for Ping. (support icmp/tcp/http)
--ping-mode Select a method for Ping (support icmp/tcp/http).
-u --unit Set human-readable and auto-scaled rate units for output
(options: decimal-bits/decimal-bytes/binary-bits/binary-bytes).
-d --debug Enable debug mode.
--version Show application version.
```
Expand All @@ -68,15 +70,15 @@ Simply use `speedtest` command. The closest server is selected by default. Use t
```bash
$ speedtest

speedtest-go v1.6.5 @showwin
speedtest-go v1.7.0 @showwin

✓ ISP: 124.27.199.165 (Fujitsu) [34.9769, 138.3831]
✓ Found 20 Public Servers

✓ Test Server: [6691] 9.03km Shizuoka (Japan) by sudosan
✓ Latency: 24.15396ms Jitter: 777.465µs Min: 22.8926ms Max: 25.5387ms
✓ Download: 73.30Mbps (used: 101.48MB)
✓ Upload: 35.26Mbps (used: 47.33MB)
✓ Latency: 4.452963ms Jitter: 41.271µs Min: 4.395179ms Max: 4.517576ms
✓ Download: 115.52 Mbps (Used: 135.75MB) (Latency: 4ms Jitter: 0ms Min: 4ms Max: 4ms)
✓ Upload: 4.02 Mbps (Used: 6.85MB) (Latency: 4ms Jitter: 1ms Min: 3ms Max: 8ms)
```

#### Test with Other Servers
Expand All @@ -98,23 +100,20 @@ and select them by id.
```bash
$ speedtest --server 6691 --server 6087

speedtest-go v1.6.5 @showwin
speedtest-go v1.7.0 @showwin

✓ ISP: 124.27.199.165 (Fujitsu) [34.9769, 138.3831]
✓ Found 20 Public Servers
✓ Found 2 Specified Public Server(s)

✓ Test Server: [6691] 9.03km Shizuoka (Japan) by sudosan
✓ Latency: 21.424ms Jitter: 1.644ms Min: 19.142ms Max: 23.926ms
✓ Download: 65.82Mbps (used: 75.48MB)
✓ Upload: 27.00Mbps (used: 36.33MB)
✓ Download: 65.82Mbps (Used: 75.48MB) (Latency: 22ms Jitter: 2ms Min: 17ms Max: 24ms)
✓ Upload: 27.00Mbps (Used: 36.33MB) (Latency: 23ms Jitter: 2ms Min: 18ms Max: 25ms)

✓ Test Server: [6087] 120.55km Fussa-shi (Japan) by Allied Telesis Capital Corporation
✓ Latency: 38.694699ms Jitter: 2.724ms Min: 36.443ms Max: 39.953ms
✓ Download: 72.24Mbps (used: 83.72MB)
✓ Upload: 29.56Mbps (used: 47.64MB)

Download Avg: 69.03 Mbit/s
Upload Avg: 28.28 Mbit/s
✓ Download: 72.24Mbps (Used: 83.72MB) (Latency: 37ms Jitter: 3ms Min: 36ms Max: 40ms)
✓ Upload: 29.56Mbps (Used: 47.64MB) (Latency: 38ms Jitter: 3ms Min: 37ms Max: 41ms)
```

#### Test with a virtual location
Expand Down Expand Up @@ -195,7 +194,8 @@ func main() {
s.PingTest(nil)
s.DownloadTest()
s.UploadTest()
fmt.Printf("Latency: %s, Download: %f, Upload: %f\n", s.Latency, s.DLSpeed, s.ULSpeed)
// Note: The unit of s.DLSpeed, s.ULSpeed is bytes per second, this is a float64.
fmt.Printf("Latency: %s, Download: %s, Upload: %s\n", s.Latency, s.DLSpeed, s.ULSpeed)
s.Context.Reset() // reset counter
}
}
Expand Down
3 changes: 2 additions & 1 deletion example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ func main() {
checkError(s.DownloadTest())
checkError(s.UploadTest())

fmt.Printf("Latency: %s, Download: %f, Upload: %f\n", s.Latency, s.DLSpeed, s.ULSpeed)
// Note: The unit of s.DLSpeed, s.ULSpeed is bytes per second, this is a float64.
fmt.Printf("Latency: %s, Download: %s, Upload: %s\n", s.Latency, s.DLSpeed, s.ULSpeed)
s.Context.Reset()
}
}
Expand Down
42 changes: 30 additions & 12 deletions speedtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,23 @@ import (
var (
showList = kingpin.Flag("list", "Show available speedtest.net servers.").Short('l').Bool()
serverIds = kingpin.Flag("server", "Select server id to run speedtest.").Short('s').Ints()
customURL = kingpin.Flag("custom-url", "Specify the url of the server instead of getting a list from speedtest.net.").String()
customURL = kingpin.Flag("custom-url", "Specify the url of the server instead of fetching from speedtest.net.").String()
savingMode = kingpin.Flag("saving-mode", "Test with few resources, though low accuracy (especially > 30Mbps).").Bool()
jsonOutput = kingpin.Flag("json", "Output results in json format.").Bool()
location = kingpin.Flag("location", "Change the location with a precise coordinate. Format: lat,lon").String()
location = kingpin.Flag("location", "Change the location with a precise coordinate (format: lat,lon).").String()
city = kingpin.Flag("city", "Change the location with a predefined city label.").String()
showCityList = kingpin.Flag("city-list", "List all predefined city labels.").Bool()
proxy = kingpin.Flag("proxy", "Set a proxy(http[s] or socks) for the speedtest.").String()
source = kingpin.Flag("source", "Bind a source interface for the speedtest.").String()
dnsBindSource = kingpin.Flag("dns-bind-source", "DNS request binding source.(Experimental)").Bool()
dnsBindSource = kingpin.Flag("dns-bind-source", "DNS request binding source (experimental).").Bool()
multi = kingpin.Flag("multi", "Enable multi-server mode.").Short('m').Bool()
thread = kingpin.Flag("thread", "Set the number of concurrent connections.").Short('t').Int()
search = kingpin.Flag("search", "Fuzzy search servers by a keyword.").String()
userAgent = kingpin.Flag("ua", "Set the user-agent header for the speedtest.").String()
noDownload = kingpin.Flag("no-download", "Disable download test.").Bool()
noUpload = kingpin.Flag("no-upload", "Disable upload test.").Bool()
pingMode = kingpin.Flag("ping-mode", "Select a method for Ping. (support icmp/tcp/http)").Default("http").String()
pingMode = kingpin.Flag("ping-mode", "Select a method for Ping (support icmp/tcp/http).").Default("http").String()
unit = kingpin.Flag("unit", "Set human-readable and auto-scaled rate units for output (options: decimal-bits/decimal-bytes/binary-bits/binary-bytes).").Short('u').String()
debug = kingpin.Flag("debug", "Enable debug mode.").Short('d').Bool()
)

Expand All @@ -41,6 +42,8 @@ func main() {
kingpin.Parse()
AppInfo()

speedtest.SetUnit(parseUnit(*unit))

// 0. speed test setting
var speedtestClient = speedtest.New(speedtest.WithUserConfig(
&speedtest.UserConfig{
Expand Down Expand Up @@ -128,12 +131,12 @@ func main() {
accEcho := newAccompanyEcho(server, time.Millisecond*500)
taskManager.Run("Download", func(task *Task) {
accEcho.Run()
speedtestClient.SetCallbackDownload(func(downRate float64) {
speedtestClient.SetCallbackDownload(func(downRate speedtest.ByteRate) {
lc := accEcho.CurrentLatency()
if lc == 0 {
task.Printf("Download: %.2fMbps (latency: --)", downRate*8/1000000)
task.Printf("Download: %s (Latency: --)", downRate)
} else {
task.Printf("Download: %.2fMbps (latency: %dms)", downRate*8/1000000, lc/1000000)
task.Printf("Download: %s (Latency: %dms)", downRate, lc/1000000)
}
})
if *multi {
Expand All @@ -143,18 +146,18 @@ func main() {
}
accEcho.Stop()
mean, _, std, minL, maxL := speedtest.StandardDeviation(accEcho.Latencies())
task.Printf("Download: %.2fMbps (used: %.2fMB) (latency: %dms jitter: %dms min: %dms max: %dms)", server.DLSpeed*8/1000000, float64(server.Context.Manager.GetTotalDownload())/1000/1000, mean/1000000, std/1000000, minL/1000000, maxL/1000000)
task.Printf("Download: %s (Used: %.2fMB) (Latency: %dms Jitter: %dms Min: %dms Max: %dms)", server.DLSpeed, float64(server.Context.Manager.GetTotalDownload())/1000/1000, mean/1000000, std/1000000, minL/1000000, maxL/1000000)
task.Complete()
})

taskManager.Run("Upload", func(task *Task) {
accEcho.Run()
speedtestClient.SetCallbackUpload(func(upRate float64) {
speedtestClient.SetCallbackUpload(func(upRate speedtest.ByteRate) {
lc := accEcho.CurrentLatency()
if lc == 0 {
task.Printf("Upload: %.2fMbps (latency: --)", upRate*8/1000000)
task.Printf("Upload: %s (Latency: --)", upRate)
} else {
task.Printf("Upload: %.2fMbps (latency: %dms)", upRate*8/1000000, lc/1000000)
task.Printf("Upload: %s (Latency: %dms)", upRate, lc/1000000)
}
})
if *multi {
Expand All @@ -164,7 +167,7 @@ func main() {
}
accEcho.Stop()
mean, _, std, minL, maxL := speedtest.StandardDeviation(accEcho.Latencies())
task.Printf("Upload: %.2fMbps (used: %.2fMB) (latency: %dms jitter: %dms min: %dms max: %dms)", server.ULSpeed*8/1000000, float64(server.Context.Manager.GetTotalUpload())/1000/1000, mean/1000000, std/1000000, minL/1000000, maxL/1000000)
task.Printf("Upload: %s (Used: %.2fMB) (Latency: %dms Jitter: %dms Min: %dms Max: %dms)", server.ULSpeed, float64(server.Context.Manager.GetTotalUpload())/1000/1000, mean/1000000, std/1000000, minL/1000000, maxL/1000000)
task.Complete()
})
taskManager.Reset()
Expand Down Expand Up @@ -243,6 +246,21 @@ func showServerList(servers speedtest.Servers) {
}
}

func parseUnit(str string) speedtest.UnitType {
str = strings.ToLower(str)
if str == "decimal-bits" {
return speedtest.UnitTypeDecimalBits
} else if str == "decimal-bytes" {
return speedtest.UnitTypeDecimalBytes
} else if str == "binary-bits" {
return speedtest.UnitTypeBinaryBits
} else if str == "binary-bytes" {
return speedtest.UnitTypeBinaryBytes
} else {
return speedtest.UnitTypeDefaultMbps
}
}

func parseProto(str string) speedtest.Proto {
str = strings.ToLower(str)
if str == "icmp" {
Expand Down
26 changes: 13 additions & 13 deletions speedtest/data_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ type Manager interface {
GetEWMADownloadRate() float64
GetEWMAUploadRate() float64

SetCallbackDownload(callback func(downRate float64))
SetCallbackUpload(callback func(upRate float64))
SetCallbackDownload(callback func(downRate ByteRate))
SetCallbackUpload(callback func(upRate ByteRate))

RegisterDownloadHandler(fn func()) *TestDirection
RegisterUploadHandler(fn func()) *TestDirection
Expand Down Expand Up @@ -95,14 +95,14 @@ type DataManager struct {
}

type TestDirection struct {
TestType int // test type
manager *DataManager // manager
totalDataVolume int64 // total send/receive data volume
RateSequence []int64 // rate history sequence
welford *internal.Welford // std/EWMA/mean
captureCallback func(realTimeRate float64) // user callback
closeFunc func() // close func
*funcGroup // actually exec function
TestType int // test type
manager *DataManager // manager
totalDataVolume int64 // total send/receive data volume
RateSequence []int64 // rate history sequence
welford *internal.Welford // std/EWMA/mean
captureCallback func(realTimeRate ByteRate) // user callback
closeFunc func() // close func
*funcGroup // actually exec function
}

func (dm *DataManager) NewDataDirection(testType int) *TestDirection {
Expand All @@ -126,13 +126,13 @@ func NewDataManager() *DataManager {
return ret
}

func (dm *DataManager) SetCallbackDownload(callback func(downRate float64)) {
func (dm *DataManager) SetCallbackDownload(callback func(downRate ByteRate)) {
if dm.download != nil {
dm.download.captureCallback = callback
}
}

func (dm *DataManager) SetCallbackUpload(callback func(upRate float64)) {
func (dm *DataManager) SetCallbackUpload(callback func(upRate ByteRate)) {
if dm.upload != nil {
dm.upload.captureCallback = callback
}
Expand Down Expand Up @@ -268,7 +268,7 @@ func (td *TestDirection) rateCapture() chan bool {
}
// reports the current rate at the given rate
if td.captureCallback != nil {
td.captureCallback(td.welford.EWMA())
td.captureCallback(ByteRate(td.welford.EWMA()))
}
case stop := <-stopCapture:
if stop {
Expand Down
8 changes: 4 additions & 4 deletions speedtest/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (s *Server) MultiDownloadTestContext(ctx context.Context, servers Servers)
return ErrorUninitializedManager
}
td.Start(cancel, mainIDIndex) // block here
s.DLSpeed = td.manager.GetEWMADownloadRate()
s.DLSpeed = ByteRate(td.manager.GetEWMADownloadRate())
return nil
}

Expand Down Expand Up @@ -87,7 +87,7 @@ func (s *Server) MultiUploadTestContext(ctx context.Context, servers Servers) er
return ErrorUninitializedManager
}
td.Start(cancel, mainIDIndex) // block here
s.ULSpeed = td.manager.GetEWMAUploadRate()
s.ULSpeed = ByteRate(td.manager.GetEWMAUploadRate())
return nil
}

Expand All @@ -112,7 +112,7 @@ func (s *Server) downloadTestContext(ctx context.Context, downloadRequest downlo
_ = downloadRequest(_context, s, 3)
}).Start(cancel, 0)
duration := time.Since(start)
s.DLSpeed = s.Context.GetEWMADownloadRate()
s.DLSpeed = ByteRate(s.Context.GetEWMADownloadRate())
s.TestDuration.Download = &duration
s.testDurationTotalCount()
return nil
Expand All @@ -139,7 +139,7 @@ func (s *Server) uploadTestContext(ctx context.Context, uploadRequest uploadFunc
_ = uploadRequest(_context, s, 4)
}).Start(cancel, 0)
duration := time.Since(start)
s.ULSpeed = s.Context.GetEWMAUploadRate()
s.ULSpeed = ByteRate(s.Context.GetEWMAUploadRate())
s.TestDuration.Upload = &duration
s.testDurationTotalCount()
return nil
Expand Down
4 changes: 2 additions & 2 deletions speedtest/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ type Server struct {
MaxLatency time.Duration `json:"max_latency"`
MinLatency time.Duration `json:"min_latency"`
Jitter time.Duration `json:"jitter"`
DLSpeed float64 `json:"dl_speed"`
ULSpeed float64 `json:"ul_speed"`
DLSpeed ByteRate `json:"dl_speed"`
ULSpeed ByteRate `json:"ul_speed"`
TestDuration TestDuration `json:"test_duration"`

Context *Speedtest `json:"-"`
Expand Down
Loading
Loading