Skip to content

Commit

Permalink
Merge pull request #161 from showwin/fix/stddeviation
Browse files Browse the repository at this point in the history
chore: remove redundant field url2
fix: parse uppercase for ping-mode flag
fix: unexpected division by a zero-length vector in stddeviation
chore: use http ping as default
fix: user agent is overwritten with empty string
fix(cli): panic when json failed to create
fix: use haversine formula to calculate creat-circle distance avoid precision overflow
chore: add haversine formula test
chore: unreachable lint
  • Loading branch information
r3inbowari authored Nov 12, 2023
2 parents e0f8f6c + 18626ef commit dfe5b15
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 19 deletions.
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

0 comments on commit dfe5b15

Please sign in to comment.