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

fix(cli): std deviation with zero-length vector #161

Merged
merged 10 commits into from
Nov 12, 2023
12 changes: 7 additions & 5 deletions speedtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"gopkg.in/alecthomas/kingpin.v2"
"os"
"strconv"
"strings"
"time"

"github.com/showwin/speedtest-go/speedtest"
Expand Down Expand Up @@ -151,8 +152,8 @@ func main() {
task.CheckError(server.DownloadTest())
}
ticker.Stop()
mean, _, std, min, max := speedtest.StandardDeviation(latencies)
task.Printf("Download: %.2fMbps (used: %.2fMB) (latency: %dms jitter: %dms min: %dms max: %dms)", server.DLSpeed, float64(server.Context.Manager.GetTotalDownload())/1024/1024, mean/1000000, std/1000000, min/1000000, max/1000000)
mean, _, std, minL, maxL := speedtest.StandardDeviation(latencies)
task.Printf("Download: %.2fMbps (used: %.2fMB) (latency: %dms jitter: %dms min: %dms max: %dms)", server.DLSpeed, float64(server.Context.Manager.GetTotalDownload())/1024/1024, mean/1000000, std/1000000, minL/1000000, maxL/1000000)
task.Complete()
})

Expand Down Expand Up @@ -187,8 +188,8 @@ func main() {
}
ticker.Stop()
quit = true
mean, _, std, min, max := speedtest.StandardDeviation(latencies)
task.Printf("Upload: %.2fMbps (used: %.2fMB) (latency: %dms jitter: %dms min: %dms max: %dms)", server.ULSpeed, float64(server.Context.Manager.GetTotalUpload())/1024/1024, mean/1000000, std/1000000, min/1000000, max/1000000)
mean, _, std, minL, maxL := speedtest.StandardDeviation(latencies)
task.Printf("Upload: %.2fMbps (used: %.2fMB) (latency: %dms jitter: %dms min: %dms max: %dms)", server.ULSpeed, float64(server.Context.Manager.GetTotalUpload())/1024/1024, mean/1000000, std/1000000, minL/1000000, maxL/1000000)
task.Complete()
})
taskManager.Reset()
Expand All @@ -200,7 +201,7 @@ func main() {
if *jsonOutput {
json, errMarshal := speedtestClient.JSON(targets)
if errMarshal != nil {
return
panic(errMarshal)
}
fmt.Print(string(json))
}
Expand All @@ -220,6 +221,7 @@ func showServerList(servers speedtest.Servers) {
}

func parseProto(str string) speedtest.Proto {
str = strings.ToLower(str)
if str == "icmp" {
return speedtest.ICMP
} else if str == "tcp" {
Expand Down
3 changes: 3 additions & 0 deletions speedtest/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,9 @@ func checkSum(data []byte) uint16 {
}

func StandardDeviation(vector []int64) (mean, variance, stdDev, min, max int64) {
if len(vector) == 0 {
return
}
var sumNum, accumulate int64
min = math.MaxInt64
max = math.MinInt64
Expand Down
14 changes: 7 additions & 7 deletions speedtest/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ type Server struct {
Country string `xml:"country,attr" json:"country"`
Sponsor string `xml:"sponsor,attr" json:"sponsor"`
ID string `xml:"id,attr" json:"id"`
URL2 string `xml:"url2,attr" json:"url_2"`
Host string `xml:"host,attr" json:"host"`
Distance float64 `json:"distance"`
Latency time.Duration `json:"latency"`
Expand Down Expand Up @@ -332,13 +331,14 @@ func FetchServerListContext(ctx context.Context) (Servers, error) {
func distance(lat1 float64, lon1 float64, lat2 float64, lon2 float64) float64 {
radius := 6378.137

a1 := lat1 * math.Pi / 180.0
b1 := lon1 * math.Pi / 180.0
a2 := lat2 * math.Pi / 180.0
b2 := lon2 * math.Pi / 180.0
phi1 := lat1 * math.Pi / 180.0
phi2 := lat2 * math.Pi / 180.0

x := math.Sin(a1)*math.Sin(a2) + math.Cos(a1)*math.Cos(a2)*math.Cos(b2-b1)
return radius * math.Acos(x)
deltaPhiHalf := (lat1 - lat2) * math.Pi / 360.0
deltaLambdaHalf := (lon1 - lon2) * math.Pi / 360.0
sinePhiHalf2 := math.Sin(deltaPhiHalf)*math.Sin(deltaPhiHalf) + math.Cos(phi1)*math.Cos(phi2)*math.Sin(deltaLambdaHalf)*math.Sin(deltaLambdaHalf) // phi half-angle sine ^ 2
delta := 2 * math.Atan2(math.Sqrt(sinePhiHalf2), math.Sqrt(1-sinePhiHalf2)) // 2 arc sine
return radius * delta // r * delta
}

// FindServer finds server by serverID in given server list.
Expand Down
39 changes: 35 additions & 4 deletions speedtest/server_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package speedtest

import (
"math"
"math/rand"
"testing"
"time"
)
Expand All @@ -26,26 +28,55 @@ func TestFetchServerList(t *testing.T) {
}
}

func TestDistanceSame(t *testing.T) {
for i := 0; i < 10000000; i++ {
v1 := rand.Float64() * 90
v2 := rand.Float64() * 180
v3 := rand.Float64() * 90
v4 := rand.Float64() * 180
k1 := distance(v1, v2, v1, v2)
k2 := distance(v1, v2, v3, v4)
if math.IsNaN(k1) || math.IsNaN(k2) {
t.Fatalf("NaN distance: %f, %f, %f, %f", v1, v2, v3, v4)
}
}

testdata := [][]float64{
{32.0803, 34.7805, 32.0803, 34.7805},
{0, 0, 0, 0},
{1, 1, 1, 1},
{2, 2, 2, 2},
{-123.23, 123.33, -123.23, 123.33},
{90, 180, 90, 180},
}
for i := range testdata {
k := distance(testdata[i][0], testdata[i][1], testdata[i][2], testdata[i][3])
if math.IsNaN(k) {
t.Fatalf("NaN distance: %f, %f, %f, %f", testdata[i][0], testdata[i][1], testdata[i][2], testdata[i][3])
}
}
}

func TestDistance(t *testing.T) {
d := distance(0.0, 0.0, 1.0, 1.0)
if d < 157 || 158 < d {
t.Errorf("got: %v, expected between 157 and 158", d)
}

d = distance(0.0, 180.0, 0.0, -180.0)
if d != 0 {
if d < 0 && d > 1 {
t.Errorf("got: %v, expected 0", d)
}

d1 := distance(100.0, 100.0, 100.0, 101.0)
d2 := distance(100.0, 100.0, 100.0, 99.0)
if d1 != d2 {
t.Errorf("%v and %v should be save value", d1, d2)
t.Errorf("%v and %v should be same value", d1, d2)
}

d = distance(35.0, 140.0, -40.0, -140.0)
if d < 11000 || 12000 < d {
t.Errorf("got: %v, expected 0", d)
t.Errorf("got: %v, expected ~11694.5122", d)
}
}

Expand Down Expand Up @@ -138,7 +169,7 @@ func TestFetchServerByID(t *testing.T) {
if server != nil && (server.ID == id) != b {
t.Errorf("id %s == %s is not %v", id, server.ID, b)
}
}
}
}

func TestTotalDurationCount(t *testing.T) {
Expand Down
9 changes: 6 additions & 3 deletions speedtest/speedtest.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,16 @@ import (
)

var (
version = "1.6.7"
version = "1.6.8"
DefaultUserAgent = fmt.Sprintf("showwin/speedtest-go %s", version)
)

type Proto int

const (
ICMP Proto = iota
HTTP Proto = iota
TCP
HTTP
ICMP
)

// Speedtest is a speedtest client.
Expand Down Expand Up @@ -90,6 +90,9 @@ func (s *Speedtest) NewUserConfig(uc *UserConfig) {
var icmpSource net.Addr
var proxy = http.ProxyFromEnvironment
s.config = uc
if len(s.config.UserAgent) == 0 {
s.config.UserAgent = DefaultUserAgent
}
if len(uc.Source) > 0 {
_, address := parseAddr(uc.Source)
addr0, err := net.ResolveTCPAddr("tcp", fmt.Sprintf("[%s]:0", address)) // dynamic tcp port
Expand Down
Loading