Switch from kn to m/s as default unit for Speed types

This commit is contained in:
Winni Neessen 2023-06-23 11:52:36 +02:00
parent b77cd98484
commit a7feae910b
Signed by: wneessen
GPG key ID: 5F3AF39B820C119D
3 changed files with 73 additions and 51 deletions

View file

@ -59,9 +59,9 @@ type APIObservationData struct {
Precipitation1h *APIFloat `json:"prec1h"`
// Precipitation24h represents the amount of precipitation over the last 24 hours
Precipitation24h *APIFloat `json:"prec24h"`
// PressureMSL represents the pressure at mean sea level (MSL) in hPa
// PressureMSL represents the air pressure at MSL / temperature adjusted (QFF) in hPa
PressureMSL *APIFloat `json:"pressureMsl"`
// PressureMSL represents the pressure at station level (QFE) in hPa
// PressureQFE represents the pressure at station level (QFE) in hPa
PressureQFE *APIFloat `json:"pressure"`
// Temperature represents the temperature in °C
Temperature *APIFloat `json:"temp,omitempty"`
@ -365,11 +365,11 @@ func (o Observation) GlobalRadiation(ts Timespan) Radiation {
}
}
// Winddirection returns the current direction from which the wind
// WindDirection returns the current direction from which the wind
// originates in degree (0=N, 90=E, 180=S, 270=W) as Direction.
// If the data point is not available in the Observation it will return
// Direction in which the "not available" field will be true.
func (o Observation) Winddirection() Direction {
func (o Observation) WindDirection() Direction {
if o.Data.Winddirection == nil {
return Direction{na: true}
}
@ -381,10 +381,10 @@ func (o Observation) Winddirection() Direction {
}
}
// Windspeed returns the current windspeed data point as Speed.
// WindSpeed returns the current windspeed data point as Speed.
// If the data point is not available in the Observation it will return
// Speed in which the "not available" field will be true.
func (o Observation) Windspeed() Speed {
func (o Observation) WindSpeed() Speed {
if o.Data.Windspeed == nil {
return Speed{na: true}
}
@ -392,6 +392,6 @@ func (o Observation) Windspeed() Speed {
dt: o.Data.Windspeed.DateTime,
n: FieldWindspeed,
s: SourceObservation,
fv: o.Data.Windspeed.Value,
fv: o.Data.Windspeed.Value * 0.5144444444,
}
}

View file

@ -1124,7 +1124,7 @@ func TestClient_ObservationLatestByStationID_GlobalRadiation24h(t *testing.T) {
}
}
func TestClient_ObservationLatestByStationID_Winddirection(t *testing.T) {
func TestClient_ObservationLatestByStationID_WindDirection(t *testing.T) {
tt := []struct {
// Test name
n string
@ -1153,37 +1153,37 @@ func TestClient_ObservationLatestByStationID_Winddirection(t *testing.T) {
t.Errorf("ObservationLatestByStationID with station %s failed: %s", tc.sid, err)
return
}
if tc.p != nil && tc.p.String() != o.Winddirection().String() {
if tc.p != nil && tc.p.String() != o.WindDirection().String() {
t.Errorf("ObservationLatestByStationID failed, expected wind direction "+
"string: %s, got: %s", tc.p.String(), o.Winddirection())
"string: %s, got: %s", tc.p.String(), o.WindDirection())
}
if tc.p != nil && tc.p.Value() != o.Winddirection().Value() {
if tc.p != nil && tc.p.Value() != o.WindDirection().Value() {
t.Errorf("ObservationLatestByStationID failed, expected wind direction "+
"float: %f, got: %f", tc.p.Value(), o.Winddirection().Value())
"float: %f, got: %f", tc.p.Value(), o.WindDirection().Value())
}
if tc.p != nil && tc.p.dt.Unix() != o.Winddirection().DateTime().Unix() {
if tc.p != nil && tc.p.dt.Unix() != o.WindDirection().DateTime().Unix() {
t.Errorf("ObservationLatestByStationID failed, expected datetime: %s, got: %s",
tc.p.dt.Format(time.RFC3339), o.Winddirection().DateTime().Format(time.RFC3339))
tc.p.dt.Format(time.RFC3339), o.WindDirection().DateTime().Format(time.RFC3339))
}
if o.Winddirection().Source() != SourceObservation {
if o.WindDirection().Source() != SourceObservation {
t.Errorf("ObservationLatestByStationID failed, expected observation source, but got: %s",
o.Winddirection().Source())
o.WindDirection().Source())
}
if tc.p == nil {
if o.Winddirection().IsAvailable() {
if o.WindDirection().IsAvailable() {
t.Errorf("ObservationLatestByStationID failed, expected wind direction "+
"to have no data, but got: %s", o.Winddirection())
"to have no data, but got: %s", o.WindDirection())
}
if !math.IsNaN(o.Winddirection().Value()) {
if !math.IsNaN(o.WindDirection().Value()) {
t.Errorf("ObservationLatestByStationID failed, expected wind direction "+
"to return NaN, but got: %s", o.Winddirection().String())
"to return NaN, but got: %s", o.WindDirection().String())
}
}
})
}
}
func TestClient_ObservationLatestByStationID_Windspeed(t *testing.T) {
func TestClient_ObservationLatestByStationID_WindSpeed(t *testing.T) {
tt := []struct {
// Test name
n string
@ -1196,7 +1196,7 @@ func TestClient_ObservationLatestByStationID_Windspeed(t *testing.T) {
{"K-Stammheim", "H744", nil},
{"All data fields", "all", &Speed{
dt: time.Date(2023, 0o5, 21, 11, 30, 0, 0, time.UTC),
fv: 15,
fv: 7.716666666,
}},
{"No data fields", "none", nil},
}
@ -1212,30 +1212,30 @@ func TestClient_ObservationLatestByStationID_Windspeed(t *testing.T) {
t.Errorf("ObservationLatestByStationID with station %s failed: %s", tc.sid, err)
return
}
if tc.p != nil && tc.p.String() != o.Windspeed().String() {
if tc.p != nil && tc.p.String() != o.WindSpeed().String() {
t.Errorf("ObservationLatestByStationID failed, expected windspeed "+
"string: %s, got: %s", tc.p.String(), o.Windspeed())
"string: %s, got: %s", tc.p.String(), o.WindSpeed())
}
if tc.p != nil && tc.p.Value() != o.Windspeed().Value() {
if tc.p != nil && tc.p.Value() != o.WindSpeed().Value() {
t.Errorf("ObservationLatestByStationID failed, expected windspeed "+
"float: %f, got: %f", tc.p.Value(), o.Windspeed().Value())
"float: %f, got: %f, %+v", tc.p.Value(), o.WindSpeed().Value(), o.Data.Windspeed)
}
if tc.p != nil && tc.p.dt.Unix() != o.Windspeed().DateTime().Unix() {
if tc.p != nil && tc.p.dt.Unix() != o.WindSpeed().DateTime().Unix() {
t.Errorf("ObservationLatestByStationID failed, expected datetime: %s, got: %s",
tc.p.dt.Format(time.RFC3339), o.Windspeed().DateTime().Format(time.RFC3339))
tc.p.dt.Format(time.RFC3339), o.WindSpeed().DateTime().Format(time.RFC3339))
}
if o.Windspeed().Source() != SourceObservation {
if o.WindSpeed().Source() != SourceObservation {
t.Errorf("ObservationLatestByStationID failed, expected observation source, but got: %s",
o.Windspeed().Source())
o.WindSpeed().Source())
}
if tc.p == nil {
if o.Windspeed().IsAvailable() {
if o.WindSpeed().IsAvailable() {
t.Errorf("ObservationLatestByStationID failed, expected windspeed "+
"to have no data, but got: %s", o.Windspeed())
"to have no data, but got: %s", o.WindSpeed())
}
if !math.IsNaN(o.Windspeed().Value()) {
if !math.IsNaN(o.WindSpeed().Value()) {
t.Errorf("ObservationLatestByStationID failed, expected windspeed "+
"to return NaN, but got: %s", o.Windspeed().String())
"to return NaN, but got: %s", o.WindSpeed().String())
}
}
})
@ -1308,32 +1308,35 @@ func TestObservationTemperature_String(t *testing.T) {
func TestObservationSpeed_Conversion(t *testing.T) {
tt := []struct {
// Original knots value
kn float64
// Original m/s value
ms float64
// km/h value
kmh float64
// mi/h value
mph float64
// knots value
kn float64
}{
{0, 0, 0},
{1, 1.852, 1.151},
{10, 18.52, 11.51},
{15, 27.78, 17.265},
{30, 55.56, 34.53},
{0, 0, 0, 0},
{1, 3.6, 2.236936, 1.9438444924},
{10, 36, 22.369360, 19.438444924},
{15, 54, 33.554040, 29.157667386},
{30, 108, 67.108080, 58.315334772},
}
msf := "%.1fm/s"
knf := "%.0fkn"
kmhf := "%.1fkm/h"
mphf := "%.1fmi/h"
for _, tc := range tt {
t.Run(fmt.Sprintf("%.0fkn", tc.kn), func(t *testing.T) {
os := Speed{fv: tc.kn}
if os.Value() != tc.kn {
t.Errorf("Speed.Value failed, expected: %f, got: %f", tc.kn,
t.Run(fmt.Sprintf("%.0fm/s", tc.ms), func(t *testing.T) {
os := Speed{fv: tc.ms}
if os.Value() != tc.ms {
t.Errorf("Speed.Value failed, expected: %f, got: %f", tc.ms,
os.Value())
}
if os.String() != fmt.Sprintf(knf, tc.kn) {
if os.String() != fmt.Sprintf(msf, tc.ms) {
t.Errorf("Speed.String failed, expected: %s, got: %s",
fmt.Sprintf(knf, tc.kn), os.String())
fmt.Sprintf(msf, tc.ms), os.String())
}
if os.KMH() != tc.kmh {
t.Errorf("Speed.KMH failed, expected: %f, got: %f", tc.kmh,
@ -1351,6 +1354,14 @@ func TestObservationSpeed_Conversion(t *testing.T) {
t.Errorf("Speed.MPHString failed, expected: %s, got: %s",
fmt.Sprintf(mphf, tc.mph), os.MPHString())
}
if os.Knots() != tc.kn {
t.Errorf("Speed.Knots failed, expected: %f, got: %f", tc.kn,
os.Knots())
}
if os.KnotsString() != fmt.Sprintf(knf, tc.kn) {
t.Errorf("Speed.KnotsString failed, expected: %s, got: %s",
fmt.Sprintf(knf, tc.kn), os.KnotsString())
}
})
}
}

View file

@ -26,7 +26,8 @@ func (s Speed) DateTime() time.Time {
return s.dt
}
// Value returns the float64 value of an Speed in knots
// Value returns the float64 value of an Speed in meters
// per second.
// If the Speed is not available in the Observation
// Vaule will return math.NaN instead.
func (s Speed) Value() float64 {
@ -38,7 +39,7 @@ func (s Speed) Value() float64 {
// String satisfies the fmt.Stringer interface for the Speed type
func (s Speed) String() string {
return fmt.Sprintf("%.0fkn", s.fv)
return fmt.Sprintf("%.1fm/s", s.fv)
}
// Source returns the Source of Speed
@ -49,7 +50,7 @@ func (s Speed) Source() Source {
// KMH returns the Speed value in km/h
func (s Speed) KMH() float64 {
return s.fv * 1.852
return s.fv * 3.6
}
// KMHString returns the Speed value as formatted string in km/h
@ -57,9 +58,19 @@ func (s Speed) KMHString() string {
return fmt.Sprintf("%.1fkm/h", s.KMH())
}
// Knots returns the Speed value in kn
func (s Speed) Knots() float64 {
return s.fv * 1.9438444924
}
// KnotsString returns the Speed value as formatted string in kn
func (s Speed) KnotsString() string {
return fmt.Sprintf("%.0fkn", s.Knots())
}
// MPH returns the Speed value in mi/h
func (s Speed) MPH() float64 {
return s.fv * 1.151
return s.fv * 2.236936
}
// MPHString returns the Speed value as formatted string in mi/h