diff --git a/speedtest.go b/speedtest.go index 43028f9..be82b40 100644 --- a/speedtest.go +++ b/speedtest.go @@ -6,6 +6,7 @@ import ( "gopkg.in/alecthomas/kingpin.v2" "os" "strconv" + "strings" "time" "github.com/showwin/speedtest-go/speedtest" @@ -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() }) @@ -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() @@ -200,7 +201,7 @@ func main() { if *jsonOutput { json, errMarshal := speedtestClient.JSON(targets) if errMarshal != nil { - return + panic(errMarshal) } fmt.Print(string(json)) } @@ -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" { diff --git a/speedtest/request.go b/speedtest/request.go index b9020e3..661514c 100644 --- a/speedtest/request.go +++ b/speedtest/request.go @@ -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 diff --git a/speedtest/server.go b/speedtest/server.go index 78b3b7f..0a592a4 100644 --- a/speedtest/server.go +++ b/speedtest/server.go @@ -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"` @@ -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. diff --git a/speedtest/server_test.go b/speedtest/server_test.go index 9cb4ede..6b48c91 100644 --- a/speedtest/server_test.go +++ b/speedtest/server_test.go @@ -1,6 +1,8 @@ package speedtest import ( + "math" + "math/rand" "testing" "time" ) @@ -26,6 +28,35 @@ 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 { @@ -33,19 +64,19 @@ func TestDistance(t *testing.T) { } 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) } } @@ -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) { diff --git a/speedtest/speedtest.go b/speedtest/speedtest.go index de00a73..99fde97 100644 --- a/speedtest/speedtest.go +++ b/speedtest/speedtest.go @@ -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. @@ -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