aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--main.go47
-rw-r--r--mass.go86
-rw-r--r--ui.go120
3 files changed, 232 insertions, 21 deletions
diff --git a/main.go b/main.go
index 80d0e03..516978b 100644
--- a/main.go
+++ b/main.go
@@ -4,10 +4,16 @@ import (
"fmt"
g "github.com/AllenDang/giu"
"os"
+ "time"
)
-// numPoints is the number of datapoints on the compressor map.
-const numPoints = 6
+const (
+ // numPoints is the number of datapoints on the compressor map.
+ numPoints = 6
+
+ gasConstant = 8.314472
+ airMolarMass = 0.0289647 // kg/mol
+)
func check(err error) {
if err != nil {
@@ -69,6 +75,42 @@ func init() {
}
}
+var (
+ engineMassFlowRate [numPoints]massFlowRate
+
+ // selectedMassFlowRateUnit is used to index massFlowRateUnitStrings.
+ selectedMassFlowRateUnit = defaultMassFlowRateUnitIndex
+)
+
+func massFlowRateAt(point int) massFlowRate {
+ rpm := float32(engineSpeed[point])
+ disp := displacement.asUnit(cubicMetre)
+ ve := float32(volumetricEfficiency[point]) / 100.0
+ cubicMetresPerMin := (rpm / 2.0) * disp * ve
+
+ iat, err := intakeAirTemperature[point].asUnit(kelvin)
+ check(err)
+ pres := manifoldPressure[point].asUnit(pascal)
+ molsPerMin := (pres * cubicMetresPerMin) / (gasConstant * iat)
+
+ kgPerMin := molsPerMin * airMolarMass
+
+ massPerMin := mass{kgPerMin, kilogram}
+
+ u, err := massFlowRateUnitFromString(massFlowRateUnitStrings()[selectedMassFlowRateUnit])
+ check(err)
+
+ mfr, err := newMassFlowRate(massPerMin, time.Minute, u)
+ check(err)
+ return mfr
+}
+
+func init() {
+ for i := 0; i < numPoints; i++ {
+ engineMassFlowRate[i] = massFlowRateAt(i)
+ }
+}
+
func loop() {
g.SingleWindow().Layout(
engineDisplacementRow(),
@@ -79,6 +121,7 @@ func loop() {
intakeAirTemperatureRow(),
manifoldPressureRow(),
pressureRatioRow(),
+ massFlowRateRow(),
).
Columns(
g.TableColumn("Parameter"),
diff --git a/mass.go b/mass.go
new file mode 100644
index 0000000..0b97020
--- /dev/null
+++ b/mass.go
@@ -0,0 +1,86 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "time"
+)
+
+type massUnit float32
+
+const (
+ gram massUnit = 1
+ kilogram massUnit = 1_000
+ pound massUnit = 453.5924
+)
+
+type mass struct {
+ val float32
+ unit massUnit
+}
+
+func (m mass) asUnit(u massUnit) float32 {
+ g := m.val * float32(m.unit) // Convert to grams.
+ return g / float32(u) // Convert to desired unit.
+}
+
+type massFlowRateUnit float32
+
+const (
+ kilogramsPerSecond massFlowRateUnit = 1
+ poundsPerMinute massFlowRateUnit = 0.007_559_872_833
+)
+
+// massFlowRateUnitStrings returns a slice of strings, each representing a
+// massFlowRateUnit.
+// This is necessary because giu.Combo only works with strings.
+func massFlowRateUnitStrings() []string {
+ return []string{"kg/s", "lb/min"}
+}
+
+const (
+ defaultMassFlowRateUnit massFlowRateUnit = kilogramsPerSecond
+ //Used to index massFlowRateUnitStrings
+ defaultMassFlowRateUnitIndex int32 = 0 // kg/s
+)
+
+func massFlowRateUnitFromString(s string) (massFlowRateUnit, error) {
+ // Each case corresponds to a value in massFlowRateUnitStrings.
+ switch s {
+ case "kg/s":
+ return kilogramsPerSecond, nil
+ case "lb/min":
+ return poundsPerMinute, nil
+ default:
+ return *new(massFlowRateUnit), errors.New(
+ fmt.Sprintf("invalid massFlowRateUnit: '%s'", s))
+ }
+}
+
+type massFlowRate struct {
+ val float32
+ unit massFlowRateUnit
+}
+
+func newMassFlowRate(m mass, t time.Duration, u massFlowRateUnit) (massFlowRate, error) {
+ switch u {
+ case kilogramsPerSecond:
+ return massFlowRate{
+ m.asUnit(kilogram) / float32(t.Seconds()),
+ u,
+ }, nil
+ case poundsPerMinute:
+ return massFlowRate{
+ m.asUnit(pound) / float32(t.Minutes()),
+ u,
+ }, nil
+ default:
+ return *new(massFlowRate), errors.New(
+ fmt.Sprintf("invalid massFlowRateUnit: '%v'", u))
+ }
+}
+
+func (fr massFlowRate) asUnit(u massFlowRateUnit) float32 {
+ kgps := fr.val * float32(fr.unit) // Convert to kilogramsPerSecond.
+ return kgps / float32(u) // Convert to desired unit.
+}
diff --git a/ui.go b/ui.go
index d72fec4..be1d00c 100644
--- a/ui.go
+++ b/ui.go
@@ -8,7 +8,11 @@ import (
func engineDisplacementRow() *g.RowWidget {
return g.Row(
g.Label("Engine Displacement"),
- g.InputFloat(&displacement.val).Format("%.2f"),
+ g.InputFloat(&displacement.val).Format("%.2f").OnChange(func() {
+ for i := 0; i < numPoints; i++ {
+ engineMassFlowRate[i] = massFlowRateAt(i)
+ }
+ }),
g.Combo(
"",
volumeUnitStrings()[selectedVolumeUnit],
@@ -31,12 +35,24 @@ func engineSpeedRow() *g.TableRowWidget {
return g.TableRow(
g.Label("Engine Speed"),
g.Label("rpm"),
- g.InputInt(&engineSpeed[0]),
- g.InputInt(&engineSpeed[1]),
- g.InputInt(&engineSpeed[2]),
- g.InputInt(&engineSpeed[3]),
- g.InputInt(&engineSpeed[4]),
- g.InputInt(&engineSpeed[5]),
+ g.InputInt(&engineSpeed[0]).OnChange(func() {
+ engineMassFlowRate[0] = massFlowRateAt(0)
+ }),
+ g.InputInt(&engineSpeed[1]).OnChange(func() {
+ engineMassFlowRate[1] = massFlowRateAt(1)
+ }),
+ g.InputInt(&engineSpeed[2]).OnChange(func() {
+ engineMassFlowRate[2] = massFlowRateAt(2)
+ }),
+ g.InputInt(&engineSpeed[3]).OnChange(func() {
+ engineMassFlowRate[3] = massFlowRateAt(3)
+ }),
+ g.InputInt(&engineSpeed[4]).OnChange(func() {
+ engineMassFlowRate[4] = massFlowRateAt(4)
+ }),
+ g.InputInt(&engineSpeed[5]).OnChange(func() {
+ engineMassFlowRate[5] = massFlowRateAt(5)
+ }),
)
}
@@ -44,12 +60,24 @@ func volumetricEfficiencyRow() *g.TableRowWidget {
return g.TableRow(
g.Label("Volumetric Efficiency"),
g.Label("%"),
- g.InputInt(&volumetricEfficiency[0]),
- g.InputInt(&volumetricEfficiency[1]),
- g.InputInt(&volumetricEfficiency[2]),
- g.InputInt(&volumetricEfficiency[3]),
- g.InputInt(&volumetricEfficiency[4]),
- g.InputInt(&volumetricEfficiency[5]),
+ g.InputInt(&volumetricEfficiency[0]).OnChange(func() {
+ engineMassFlowRate[0] = massFlowRateAt(0)
+ }),
+ g.InputInt(&volumetricEfficiency[1]).OnChange(func() {
+ engineMassFlowRate[1] = massFlowRateAt(1)
+ }),
+ g.InputInt(&volumetricEfficiency[2]).OnChange(func() {
+ engineMassFlowRate[2] = massFlowRateAt(2)
+ }),
+ g.InputInt(&volumetricEfficiency[3]).OnChange(func() {
+ engineMassFlowRate[3] = massFlowRateAt(3)
+ }),
+ g.InputInt(&volumetricEfficiency[4]).OnChange(func() {
+ engineMassFlowRate[4] = massFlowRateAt(4)
+ }),
+ g.InputInt(&volumetricEfficiency[5]).OnChange(func() {
+ engineMassFlowRate[5] = massFlowRateAt(5)
+ }),
)
}
@@ -73,12 +101,30 @@ func intakeAirTemperatureRow() *g.TableRowWidget {
intakeAirTemperature[i] = temperature{t, u}
}
}),
- g.InputFloat(&intakeAirTemperature[0].val).Format("%.2f"),
- g.InputFloat(&intakeAirTemperature[1].val).Format("%.2f"),
- g.InputFloat(&intakeAirTemperature[2].val).Format("%.2f"),
- g.InputFloat(&intakeAirTemperature[3].val).Format("%.2f"),
- g.InputFloat(&intakeAirTemperature[4].val).Format("%.2f"),
- g.InputFloat(&intakeAirTemperature[5].val).Format("%.2f"),
+ g.InputFloat(&intakeAirTemperature[0].val).Format("%.2f").
+ OnChange(func() {
+ engineMassFlowRate[0] = massFlowRateAt(0)
+ }),
+ g.InputFloat(&intakeAirTemperature[1].val).Format("%.2f").
+ OnChange(func() {
+ engineMassFlowRate[1] = massFlowRateAt(1)
+ }),
+ g.InputFloat(&intakeAirTemperature[2].val).Format("%.2f").
+ OnChange(func() {
+ engineMassFlowRate[2] = massFlowRateAt(2)
+ }),
+ g.InputFloat(&intakeAirTemperature[3].val).Format("%.2f").
+ OnChange(func() {
+ engineMassFlowRate[3] = massFlowRateAt(3)
+ }),
+ g.InputFloat(&intakeAirTemperature[4].val).Format("%.2f").
+ OnChange(func() {
+ engineMassFlowRate[4] = massFlowRateAt(4)
+ }),
+ g.InputFloat(&intakeAirTemperature[5].val).Format("%.2f").
+ OnChange(func() {
+ engineMassFlowRate[5] = massFlowRateAt(5)
+ }),
)
}
@@ -106,26 +152,32 @@ func manifoldPressureRow() *g.TableRowWidget {
g.InputFloat(&manifoldPressure[0].val).Format("%.2f").
OnChange(func() {
pressureRatio[0] = pressureRatioAt(0)
+ engineMassFlowRate[0] = massFlowRateAt(0)
}),
g.InputFloat(&manifoldPressure[1].val).Format("%.2f").
OnChange(func() {
pressureRatio[1] = pressureRatioAt(1)
+ engineMassFlowRate[1] = massFlowRateAt(1)
}),
g.InputFloat(&manifoldPressure[2].val).Format("%.2f").
OnChange(func() {
pressureRatio[2] = pressureRatioAt(2)
+ engineMassFlowRate[2] = massFlowRateAt(2)
}),
g.InputFloat(&manifoldPressure[3].val).Format("%.2f").
OnChange(func() {
pressureRatio[3] = pressureRatioAt(3)
+ engineMassFlowRate[3] = massFlowRateAt(3)
}),
g.InputFloat(&manifoldPressure[4].val).Format("%.2f").
OnChange(func() {
pressureRatio[4] = pressureRatioAt(4)
+ engineMassFlowRate[4] = massFlowRateAt(4)
}),
g.InputFloat(&manifoldPressure[5].val).Format("%.2f").
OnChange(func() {
pressureRatio[5] = pressureRatioAt(5)
+ engineMassFlowRate[5] = massFlowRateAt(5)
}),
)
}
@@ -142,3 +194,33 @@ func pressureRatioRow() *g.TableRowWidget {
g.Label(strconv.FormatFloat(float64(pressureRatio[5]), 'f', 1, 32)),
)
}
+
+func massFlowRateRow() *g.TableRowWidget {
+ return g.TableRow(
+ g.Label("Mass Flow Rate"),
+ g.Combo(
+ "",
+ massFlowRateUnitStrings()[selectedMassFlowRateUnit],
+ massFlowRateUnitStrings(),
+ &selectedMassFlowRateUnit,
+ ).
+ OnChange(func() {
+ s := massFlowRateUnitStrings()[selectedMassFlowRateUnit]
+ u, err := massFlowRateUnitFromString(s)
+ check(err)
+
+ for i := 0; i < numPoints; i++ {
+ engineMassFlowRate[i] = massFlowRate{
+ engineMassFlowRate[i].asUnit(u),
+ u,
+ }
+ }
+ }),
+ g.Label(strconv.FormatFloat(float64(engineMassFlowRate[0].val), 'f', 3, 32)),
+ g.Label(strconv.FormatFloat(float64(engineMassFlowRate[1].val), 'f', 3, 32)),
+ g.Label(strconv.FormatFloat(float64(engineMassFlowRate[2].val), 'f', 3, 32)),
+ g.Label(strconv.FormatFloat(float64(engineMassFlowRate[3].val), 'f', 3, 32)),
+ g.Label(strconv.FormatFloat(float64(engineMassFlowRate[4].val), 'f', 3, 32)),
+ g.Label(strconv.FormatFloat(float64(engineMassFlowRate[5].val), 'f', 3, 32)),
+ )
+}