diff options
| -rw-r--r-- | main.go | 47 | ||||
| -rw-r--r-- | mass.go | 86 | ||||
| -rw-r--r-- | ui.go | 120 |
3 files changed, 232 insertions, 21 deletions
@@ -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"), @@ -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. +} @@ -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)), + ) +} |