Code development platform for open source projects from the European Union institutions

Skip to content
Snippets Groups Projects
Commit 10d08efe authored by Michael KRISPER's avatar Michael KRISPER
Browse files

Shift Polygon: Corrected Left-Of Segment Bug. Added Testcase

parent 8b0a70ee
No related branches found
No related tags found
No related merge requests found
...@@ -88,37 +88,21 @@ namespace TUGraz.VectoCore.InputData.Reader ...@@ -88,37 +88,21 @@ namespace TUGraz.VectoCore.InputData.Reader
private static List<ShiftPolygon.ShiftPolygonEntry> CreateFromColumnNames(DataTable data, string columnName) private static List<ShiftPolygon.ShiftPolygonEntry> CreateFromColumnNames(DataTable data, string columnName)
{ {
return (from DataRow row in data.Rows return (from DataRow row in data.Rows
select new ShiftPolygon.ShiftPolygonEntry { select new ShiftPolygon.ShiftPolygonEntry(row.SI<NewtonMeter>(Fields.Torque),
Torque = row.ParseDouble(Fields.Torque).SI<NewtonMeter>(), row.ParseDouble(columnName).RPMtoRad())).ToList();
AngularSpeed = row.ParseDouble(columnName).RPMtoRad(),
}).ToList();
} }
private static List<ShiftPolygon.ShiftPolygonEntry> CreateFromColumnIndizes(DataTable data, int column) private static List<ShiftPolygon.ShiftPolygonEntry> CreateFromColumnIndizes(DataTable data, int column)
{ {
return (from DataRow row in data.Rows return (from DataRow row in data.Rows
select select new ShiftPolygon.ShiftPolygonEntry(row.SI<NewtonMeter>(0), row.ParseDouble(column).RPMtoRad()))
new ShiftPolygon.ShiftPolygonEntry { .ToList();
Torque = row.ParseDouble(0).SI<NewtonMeter>(),
AngularSpeed = row.ParseDouble(column).RPMtoRad(),
}).ToList();
} }
public static class Fields public static class Fields
{ {
/// <summary>
/// [Nm] torque
/// </summary>
public const string Torque = "engine torque"; public const string Torque = "engine torque";
/// <summary>
/// [rpm] threshold for upshift
/// </summary>
public const string AngularSpeedUp = "upshift rpm"; public const string AngularSpeedUp = "upshift rpm";
/// <summary>
/// [rpm] threshold for downshift
/// </summary>
public const string AngularSpeedDown = "downshift rpm"; public const string AngularSpeedDown = "downshift rpm";
} }
} }
......
...@@ -96,7 +96,6 @@ namespace TUGraz.VectoCore.Models.Declaration ...@@ -96,7 +96,6 @@ namespace TUGraz.VectoCore.Models.Declaration
return 1; return 1;
} }
public static class Driver public static class Driver
{ {
public static class LookAhead public static class LookAhead
...@@ -177,6 +176,7 @@ namespace TUGraz.VectoCore.Models.Declaration ...@@ -177,6 +176,7 @@ namespace TUGraz.VectoCore.Models.Declaration
//public static readonly PerSecond TorqueConverterSpeedLimit = 1600.RPMtoRad(); //public static readonly PerSecond TorqueConverterSpeedLimit = 1600.RPMtoRad();
public static readonly double TorqueConverterSecondGearThreshold = 1.8; public static readonly double TorqueConverterSecondGearThreshold = 1.8;
public static readonly Second PowershiftShiftTime = 0.8.SI<Second>(); public static readonly Second PowershiftShiftTime = 0.8.SI<Second>();
/// <summary> /// <summary>
...@@ -233,11 +233,7 @@ namespace TUGraz.VectoCore.Models.Declaration ...@@ -233,11 +233,7 @@ namespace TUGraz.VectoCore.Models.Declaration
if (gearIdx > 0) { if (gearIdx > 0) {
downShift = downShift =
new[] { p2, downshiftCorr.P1, downshiftCorr.P2 }.Select( new[] { p2, downshiftCorr.P1, downshiftCorr.P2 }.Select(
point => new ShiftPolygon.ShiftPolygonEntry() { point => new ShiftPolygon.ShiftPolygonEntry(point.Y.SI<NewtonMeter>(), point.X.SI<PerSecond>())).ToList();
AngularSpeed = point.X.SI<PerSecond>(),
Torque = point.Y.SI<NewtonMeter>()
})
.ToList();
downShift[0].Torque = maxDragTorque; downShift[0].Torque = maxDragTorque;
} }
...@@ -258,11 +254,7 @@ namespace TUGraz.VectoCore.Models.Declaration ...@@ -258,11 +254,7 @@ namespace TUGraz.VectoCore.Models.Declaration
// ReSharper restore InconsistentNaming // ReSharper restore InconsistentNaming
upShift = IntersectShiftPolygon(new[] { p4, p7, p5 }, new[] { p2p, p6p, p3pExt }) upShift = IntersectShiftPolygon(new[] { p4, p7, p5 }, new[] { p2p, p6p, p3pExt })
.Select(point => new ShiftPolygon.ShiftPolygonEntry() { .Select(point => new ShiftPolygon.ShiftPolygonEntry(point.Y.SI<NewtonMeter>(), point.X.SI<PerSecond>())).ToList();
AngularSpeed = point.X.SI<PerSecond>(),
Torque = point.Y.SI<NewtonMeter>()
})
.ToList();
upShift[0].Torque = maxDragTorque; upShift[0].Torque = maxDragTorque;
return new ShiftPolygon(downShift, upShift); return new ShiftPolygon(downShift, upShift);
} }
...@@ -398,12 +390,11 @@ namespace TUGraz.VectoCore.Models.Declaration ...@@ -398,12 +390,11 @@ namespace TUGraz.VectoCore.Models.Declaration
var p2corr = new Point((maxTorque.Value() - edge.OffsetXY) / edge.SlopeXY, maxTorque.Value()); var p2corr = new Point((maxTorque.Value() - edge.OffsetXY) / edge.SlopeXY, maxTorque.Value());
var downshift = new[] { var downshift = new[] {
new ShiftPolygon.ShiftPolygonEntry { AngularSpeed = DownshiftPRM, Torque = maxDragTorque }, new ShiftPolygon.ShiftPolygonEntry(maxDragTorque, DownshiftPRM),
new ShiftPolygon.ShiftPolygonEntry { AngularSpeed = DownshiftPRM, Torque = maxTorque } new ShiftPolygon.ShiftPolygonEntry(maxTorque, DownshiftPRM)
}; };
var upshift = new[] { p0, p1, p2corr }.Select( var upshift = new[] { p0, p1, p2corr }.Select(
pt => pt => new ShiftPolygon.ShiftPolygonEntry(pt.Y.SI<NewtonMeter>(), pt.X.SI<PerSecond>()));
new ShiftPolygon.ShiftPolygonEntry { AngularSpeed = pt.X.SI<PerSecond>(), Torque = pt.Y.SI<NewtonMeter>() });
return new ShiftPolygon(first ? new List<ShiftPolygon.ShiftPolygonEntry>() : downshift.ToList(), return new ShiftPolygon(first ? new List<ShiftPolygon.ShiftPolygonEntry>() : downshift.ToList(),
last ? new List<ShiftPolygon.ShiftPolygonEntry>() : upshift.ToList()); last ? new List<ShiftPolygon.ShiftPolygonEntry>() : upshift.ToList());
...@@ -415,11 +406,11 @@ namespace TUGraz.VectoCore.Models.Declaration ...@@ -415,11 +406,11 @@ namespace TUGraz.VectoCore.Models.Declaration
var data = VectoCSVFile.ReadStream(RessourceHelper.ReadStream(resourceId), source: resourceId); var data = VectoCSVFile.ReadStream(RessourceHelper.ReadStream(resourceId), source: resourceId);
var characteristicTorque = (from DataRow row in data.Rows var characteristicTorque = (from DataRow row in data.Rows
select select
new TorqueConverterEntry() { new TorqueConverterEntry() {
SpeedRatio = row.ParseDouble(TorqueConverterDataReader.Fields.SpeedRatio), SpeedRatio = row.ParseDouble(TorqueConverterDataReader.Fields.SpeedRatio),
Torque = row.ParseDouble(TorqueConverterDataReader.Fields.CharacteristicTorque).SI<NewtonMeter>(), Torque = row.ParseDouble(TorqueConverterDataReader.Fields.CharacteristicTorque).SI<NewtonMeter>(),
TorqueRatio = row.ParseDouble(TorqueConverterDataReader.Fields.TorqueRatio) TorqueRatio = row.ParseDouble(TorqueConverterDataReader.Fields.TorqueRatio)
}).ToArray(); }).ToArray();
foreach (var torqueConverterEntry in characteristicTorque) { foreach (var torqueConverterEntry in characteristicTorque) {
torqueConverterEntry.SpeedRatio = torqueConverterEntry.SpeedRatio * ratio; torqueConverterEntry.SpeedRatio = torqueConverterEntry.SpeedRatio * ratio;
torqueConverterEntry.TorqueRatio = torqueConverterEntry.TorqueRatio / ratio; torqueConverterEntry.TorqueRatio = torqueConverterEntry.TorqueRatio / ratio;
......
/* /*
* This file is part of VECTO. * This file is part of VECTO.
* *
* Copyright © 2012-2016 European Union * Copyright © 2012-2016 European Union
* *
* Developed by Graz University of Technology, * Developed by Graz University of Technology,
* Institute of Internal Combustion Engines and Thermodynamics, * Institute of Internal Combustion Engines and Thermodynamics,
* Institute of Technical Informatics * Institute of Technical Informatics
* *
* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved * VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved
* by the European Commission - subsequent versions of the EUPL (the "Licence"); * by the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use VECTO except in compliance with the Licence. * You may not use VECTO except in compliance with the Licence.
* You may obtain a copy of the Licence at: * You may obtain a copy of the Licence at:
* *
* https://joinup.ec.europa.eu/community/eupl/og_page/eupl * https://joinup.ec.europa.eu/community/eupl/og_page/eupl
* *
* Unless required by applicable law or agreed to in writing, VECTO * Unless required by applicable law or agreed to in writing, VECTO
* distributed under the Licence is distributed on an "AS IS" basis, * distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and * See the Licence for the specific language governing permissions and
* limitations under the Licence. * limitations under the Licence.
* *
* Authors: * Authors:
* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology * Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology
* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology * Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology
* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology * Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology
* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology * Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology
* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology * Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology
* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology
*/ */
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using TUGraz.VectoCommon.Models; using TUGraz.VectoCommon.Models;
using TUGraz.VectoCommon.Utils; using TUGraz.VectoCommon.Utils;
namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox namespace TUGraz.VectoCore.Models.SimulationComponent.Data.Gearbox
{ {
[CustomValidation(typeof(ShiftPolygon), "ValidateShiftPolygon")] [CustomValidation(typeof(ShiftPolygon), "ValidateShiftPolygon")]
public class ShiftPolygon : SimulationComponentData public class ShiftPolygon : SimulationComponentData
{ {
private readonly List<ShiftPolygonEntry> _upShiftPolygon; private readonly List<ShiftPolygonEntry> _upShiftPolygon;
private readonly List<ShiftPolygonEntry> _downShiftPolygon; private readonly List<ShiftPolygonEntry> _downShiftPolygon;
internal ShiftPolygon(List<ShiftPolygonEntry> downshift, List<ShiftPolygonEntry> upShift) internal ShiftPolygon(List<ShiftPolygonEntry> downshift, List<ShiftPolygonEntry> upShift)
{ {
_upShiftPolygon = upShift; _upShiftPolygon = upShift;
_downShiftPolygon = downshift; _downShiftPolygon = downshift;
} }
public ReadOnlyCollection<ShiftPolygonEntry> Upshift
public ReadOnlyCollection<ShiftPolygonEntry> Upshift {
{ get { return _upShiftPolygon.AsReadOnly(); }
get { return _upShiftPolygon.AsReadOnly(); } }
}
public ReadOnlyCollection<ShiftPolygonEntry> Downshift
public ReadOnlyCollection<ShiftPolygonEntry> Downshift {
{ get { return _downShiftPolygon.AsReadOnly(); }
get { return _downShiftPolygon.AsReadOnly(); } }
}
public bool IsBelowDownshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity)
public bool IsBelowDownshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity) {
{ var section = Downshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity);
var section = Downshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); if (section.Item2.AngularSpeed < inAngularVelocity) {
if (section.Item2.AngularSpeed < inAngularVelocity) { return false;
return false; }
} return IsLeftOf(inAngularVelocity, inTorque, section);
return IsLeftOf(inAngularVelocity, inTorque, section); }
}
public bool IsBelowUpshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity)
public bool IsBelowUpshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity) {
{ var section = Upshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity);
var section = Upshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); if (section.Item2.AngularSpeed < inAngularVelocity) {
if (section.Item2.AngularSpeed < inAngularVelocity) { return false;
return false; }
} return IsLeftOf(inAngularVelocity, inTorque, section);
return IsLeftOf(inAngularVelocity, inTorque, section); }
}
public bool IsAboveDownshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity)
public bool IsAboveDownshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity) {
{ var section = Downshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity);
var section = Downshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity);
if (section.Item2.AngularSpeed < inAngularVelocity) {
if (section.Item2.AngularSpeed < inAngularVelocity) { return true;
return true; }
} return IsRightOf(inAngularVelocity, inTorque, section);
return IsRightOf(inAngularVelocity, inTorque, section); }
}
public bool IsAboveUpshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity)
public bool IsAboveUpshiftCurve(NewtonMeter inTorque, PerSecond inAngularVelocity) {
{ var section = Upshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity);
var section = Upshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity);
if (section.Item2.AngularSpeed < inAngularVelocity) {
if (section.Item2.AngularSpeed < inAngularVelocity) { return true;
return true; }
} return IsRightOf(inAngularVelocity, inTorque, section);
return IsRightOf(inAngularVelocity, inTorque, section); }
}
/// <summary>
/// <summary> /// Tests if current power request is on the left side of the shiftpolygon segment
/// Tests if current power request is on the left side of the shiftpolygon segment /// </summary>
/// </summary> /// <param name="angularSpeed">The angular speed.</param>
/// <param name="angularSpeed">The angular speed.</param> /// <param name="torque">The torque.</param>
/// <param name="torque">The torque.</param> /// <param name="segment">Edge of the shift polygon</param>
/// <param name="segment">Edge of the shift polygon</param> /// <returns><c>true</c> if current power request is on the left side of the shiftpolygon segment; otherwise, <c>false</c>.</returns>
/// <returns><c>true</c> if current power request is on the left side of the shiftpolygon segment; otherwise, <c>false</c>.</returns> /// <remarks>Computes a simplified cross product for the vectors: from--X, from--to and checks
/// <remarks>Computes a simplified cross product for the vectors: from--X, from--to and checks /// if the z-component is positive (which means that X was on the right side of from--to).</remarks>
/// if the z-component is positive (which means that X was on the right side of from--to).</remarks> public static bool IsLeftOf(PerSecond angularSpeed, NewtonMeter torque,
protected static bool IsLeftOf(PerSecond angularSpeed, NewtonMeter torque, Tuple<ShiftPolygonEntry, ShiftPolygonEntry> segment)
Tuple<ShiftPolygonEntry, ShiftPolygonEntry> segment) {
{ if (segment.Item1.AngularSpeed < angularSpeed && segment.Item2.AngularSpeed < angularSpeed)
var abX = segment.Item2.AngularSpeed.Value() - segment.Item1.AngularSpeed.Value(); return false;
var abY = segment.Item2.Torque.Value() - segment.Item1.Torque.Value();
var acX = angularSpeed.Value() - segment.Item1.AngularSpeed.Value(); var abX = segment.Item2.AngularSpeed.Value() - segment.Item1.AngularSpeed.Value();
var acY = torque.Value() - segment.Item1.Torque.Value(); var abY = segment.Item2.Torque.Value() - segment.Item1.Torque.Value();
var z = abX * acY - abY * acX; var acX = angularSpeed.Value() - segment.Item1.AngularSpeed.Value();
return z.IsGreater(0); var acY = torque.Value() - segment.Item1.Torque.Value();
} var z = abX * acY - abY * acX;
return z.IsGreater(0);
/// <summary> }
/// Tests if current power request is on the left side of the shiftpolygon segment
/// </summary> /// <summary>
/// <param name="angularSpeed">The angular speed.</param> /// Tests if current power request is on the left side of the shiftpolygon segment
/// <param name="torque">The torque.</param> /// </summary>
/// <param name="segment">Edge of the shift polygon</param> /// <param name="angularSpeed">The angular speed.</param>
/// <returns><c>true</c> if current power request is on the left side of the shiftpolygon segment; otherwise, <c>false</c>.</returns> /// <param name="torque">The torque.</param>
/// <remarks>Computes a simplified cross product for the vectors: from--X, from--to and checks /// <param name="segment">Edge of the shift polygon</param>
/// if the z-component is negative (which means that X was on the left side of from--to).</remarks> /// <returns><c>true</c> if current power request is on the left side of the shiftpolygon segment; otherwise, <c>false</c>.</returns>
protected static bool IsRightOf(PerSecond angularSpeed, NewtonMeter torque, /// <remarks>Computes a simplified cross product for the vectors: from--X, from--to and checks
Tuple<ShiftPolygonEntry, ShiftPolygonEntry> segment) /// if the z-component is negative (which means that X was on the left side of from--to).</remarks>
{ public static bool IsRightOf(PerSecond angularSpeed, NewtonMeter torque,
var abX = segment.Item2.AngularSpeed.Value() - segment.Item1.AngularSpeed.Value(); Tuple<ShiftPolygonEntry, ShiftPolygonEntry> segment)
var abY = segment.Item2.Torque.Value() - segment.Item1.Torque.Value(); {
var acX = angularSpeed.Value() - segment.Item1.AngularSpeed.Value(); if (segment.Item1.AngularSpeed > angularSpeed && segment.Item2.AngularSpeed > angularSpeed)
var acY = torque.Value() - segment.Item1.Torque.Value(); return false;
var z = abX * acY - abY * acX;
return z.IsSmaller(0); var abX = segment.Item2.AngularSpeed.Value() - segment.Item1.AngularSpeed.Value();
} var abY = segment.Item2.Torque.Value() - segment.Item1.Torque.Value();
var acX = angularSpeed.Value() - segment.Item1.AngularSpeed.Value();
var acY = torque.Value() - segment.Item1.Torque.Value();
// ReSharper disable once UnusedMember.Global -- used via validation var z = abX * acY - abY * acX;
public static ValidationResult ValidateShiftPolygon(ShiftPolygon shiftPolygon, ValidationContext validationContext) return z.IsSmaller(0);
{ }
var validationService =
validationContext.GetService(typeof(VectoValidationModeServiceContainer)) as VectoValidationModeServiceContainer; // ReSharper disable once UnusedMember.Global -- used via validation
var gbxType = validationService != null ? validationService.GearboxType : null; public static ValidationResult ValidateShiftPolygon(ShiftPolygon shiftPolygon, ValidationContext validationContext)
{
if (gbxType == null || gbxType.Value.AutomaticTransmission()) { var validationService =
return ValidationResult.Success; validationContext.GetService(typeof(VectoValidationModeServiceContainer)) as VectoValidationModeServiceContainer;
} var gbxType = validationService != null ? validationService.GearboxType : null;
return shiftPolygon.Downshift.Pairwise(Tuple.Create) if (gbxType == null || gbxType.Value.AutomaticTransmission()) {
.Any( return ValidationResult.Success;
downshiftLine => }
shiftPolygon.Upshift.Any(upshiftEntry => IsLeftOf(upshiftEntry.AngularSpeed, upshiftEntry.Torque, downshiftLine)))
? new ValidationResult("upshift line has to be right of the downshift line!") return shiftPolygon.Downshift.Pairwise(Tuple.Create).Any(downshiftLine =>
: ValidationResult.Success; shiftPolygon.Upshift.Any(upshiftEntry => IsLeftOf(upshiftEntry.AngularSpeed, upshiftEntry.Torque, downshiftLine)))
} ? new ValidationResult("upshift line has to be right of the downshift line!")
: ValidationResult.Success;
}
[DebuggerDisplay("{Torque}, {AngularSpeed}")]
public class ShiftPolygonEntry [DebuggerDisplay("{Torque}, {AngularSpeed}")]
{ public class ShiftPolygonEntry
/// <summary> {
/// [Nm] engine torque public ShiftPolygonEntry(NewtonMeter torque, PerSecond angularSpeed)
/// </summary> {
public NewtonMeter Torque { get; set; } Torque = torque;
AngularSpeed = angularSpeed;
/// <summary> }
/// [1/s] angular velocity threshold
/// </summary> /// <summary>
public PerSecond AngularSpeed { get; set; } /// [Nm] engine torque
} /// </summary>
public NewtonMeter Torque { get; set; }
public NewtonMeter InterpolateDownshift(PerSecond inAngularVelocity)
{ /// <summary>
var section = Downshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity); /// [1/s] angular velocity threshold
/// </summary>
if (section.Item1.AngularSpeed.IsEqual(section.Item2.AngularSpeed)) { public PerSecond AngularSpeed { get; set; }
// vertical line }
return double.MaxValue.SI<NewtonMeter>();
} public NewtonMeter InterpolateDownshift(PerSecond inAngularVelocity)
return VectoMath.Interpolate(section.Item1.AngularSpeed, section.Item2.AngularSpeed, section.Item1.Torque, {
section.Item2.Torque, inAngularVelocity); var section = Downshift.GetSection(entry => entry.AngularSpeed < inAngularVelocity);
}
} if (section.Item1.AngularSpeed.IsEqual(section.Item2.AngularSpeed)) {
// vertical line
return double.MaxValue.SI<NewtonMeter>();
}
return VectoMath.Interpolate(section.Item1.AngularSpeed, section.Item2.AngularSpeed, section.Item1.Torque,
section.Item2.Torque, inAngularVelocity);
}
}
} }
\ No newline at end of file
/* /*
* This file is part of VECTO. * This file is part of VECTO.
* *
* Copyright © 2012-2016 European Union * Copyright © 2012-2016 European Union
* *
* Developed by Graz University of Technology, * Developed by Graz University of Technology,
* Institute of Internal Combustion Engines and Thermodynamics, * Institute of Internal Combustion Engines and Thermodynamics,
* Institute of Technical Informatics * Institute of Technical Informatics
* *
* VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved * VECTO is licensed under the EUPL, Version 1.1 or - as soon they will be approved
* by the European Commission - subsequent versions of the EUPL (the "Licence"); * by the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use VECTO except in compliance with the Licence. * You may not use VECTO except in compliance with the Licence.
* You may obtain a copy of the Licence at: * You may obtain a copy of the Licence at:
* *
* https://joinup.ec.europa.eu/community/eupl/og_page/eupl * https://joinup.ec.europa.eu/community/eupl/og_page/eupl
* *
* Unless required by applicable law or agreed to in writing, VECTO * Unless required by applicable law or agreed to in writing, VECTO
* distributed under the Licence is distributed on an "AS IS" basis, * distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and * See the Licence for the specific language governing permissions and
* limitations under the Licence. * limitations under the Licence.
* *
* Authors: * Authors:
* Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology * Stefan Hausberger, hausberger@ivt.tugraz.at, IVT, Graz University of Technology
* Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology * Christian Kreiner, christian.kreiner@tugraz.at, ITI, Graz University of Technology
* Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology * Michael Krisper, michael.krisper@tugraz.at, ITI, Graz University of Technology
* Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology * Raphael Luz, luz@ivt.tugraz.at, IVT, Graz University of Technology
* Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology * Markus Quaritsch, markus.quaritsch@tugraz.at, IVT, Graz University of Technology
* Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology * Martin Rexeis, rexeis@ivt.tugraz.at, IVT, Graz University of Technology
*/ */
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Data; using System.Data;
using System.Globalization; using System.Globalization;
using TUGraz.VectoCommon.Exceptions; using TUGraz.VectoCommon.Exceptions;
using TUGraz.VectoCommon.Utils; using TUGraz.VectoCommon.Utils;
namespace TUGraz.VectoCore.Utils namespace TUGraz.VectoCore.Utils
{ {
public static class DataTableExtensionMethods public static class DataTableExtensionMethods
{ {
public static double ParseDoubleOrGetDefault(this DataRow row, string columnName, public static double ParseDoubleOrGetDefault(this DataRow row, string columnName,
double defaultValue = default(double)) double defaultValue = default(double))
{ {
if (row.Table.Columns.Contains(columnName)) { if (row.Table.Columns.Contains(columnName)) {
double result; double result;
if (double.TryParse(row.Field<string>(columnName), NumberStyles.Any, CultureInfo.InvariantCulture, out result)) { if (double.TryParse(row.Field<string>(columnName), NumberStyles.Any, CultureInfo.InvariantCulture, out result)) {
return result; return result;
} }
} }
return defaultValue; return defaultValue;
} }
public static double ParseDouble(this DataRow row, int columnIndex) public static double ParseDouble(this DataRow row, int columnIndex)
{ {
return row.ParseDouble(row.Table.Columns[columnIndex]); return row.ParseDouble(row.Table.Columns[columnIndex]);
} }
public static double ParseDouble(this DataRow row, string columnName) public static T SI<T>(this DataRow row, int columnIndex) where T : SIBase<T>
{ {
if (!row.Table.Columns.Contains(columnName)) { return row.ParseDouble(row.Table.Columns[columnIndex]).SI<T>();
throw new KeyNotFoundException(string.Format("Column {0} was not found in DataRow.", columnName)); }
}
return row.ParseDouble(row.Table.Columns[columnName]); public static T SI<T>(this DataRow row, string columnName) where T : SIBase<T>
} {
return row.ParseDouble(columnName).SI<T>();
public static double ParseDouble(this DataRow row, DataColumn column) }
{
try { public static double ParseDouble(this DataRow row, string columnName)
return row.Field<string>(column).ToDouble(); {
} catch (IndexOutOfRangeException e) { if (!row.Table.Columns.Contains(columnName)) {
throw new VectoException(string.Format("Field {0} was not found in DataRow.", column), e); throw new KeyNotFoundException(string.Format("Column {0} was not found in DataRow.", columnName));
} catch (NullReferenceException e) { }
throw new VectoException(string.Format("Field {0} must not be null.", column), e); return row.ParseDouble(row.Table.Columns[columnName]);
} catch (FormatException e) { }
throw new VectoException(string.Format("Field {0} is not in a valid number format: {1}", column,
row.Field<string>(column)), e); public static double ParseDouble(this DataRow row, DataColumn column)
} catch (OverflowException e) { {
throw new VectoException(string.Format("Field {0} has a value too high or too low: {1}", column, try {
row.Field<string>(column)), e); return row.Field<string>(column).ToDouble();
} catch (ArgumentNullException e) { } catch (IndexOutOfRangeException e) {
throw new VectoException(string.Format("Field {0} contains null which cannot be converted to a number.", column), e); throw new VectoException(string.Format("Field {0} was not found in DataRow.", column), e);
} catch (Exception e) { } catch (NullReferenceException e) {
throw new VectoException(string.Format("Field {0}: {1}", column, e.Message), e); throw new VectoException(string.Format("Field {0} must not be null.", column), e);
} } catch (FormatException e) {
} throw new VectoException(string.Format("Field {0} is not in a valid number format: {1}", column,
row.Field<string>(column)), e);
public static bool ParseBoolean(this DataRow row, string columnName) } catch (OverflowException e) {
{ throw new VectoException(string.Format("Field {0} has a value too high or too low: {1}", column,
if (!row.Table.Columns.Contains(columnName)) { row.Field<string>(column)), e);
throw new KeyNotFoundException(string.Format("Column {0} was not found in DataRow.", columnName)); } catch (ArgumentNullException e) {
} throw new VectoException(string.Format("Field {0} contains null which cannot be converted to a number.", column),
return row.Field<string>(row.Table.Columns[columnName]).ToBoolean(); e);
} } catch (Exception e) {
throw new VectoException(string.Format("Field {0}: {1}", column, e.Message), e);
public static IEnumerable<T> Values<T>(this DataColumn column) }
{ }
return column.Table.AsEnumerable().Select(r => r.Field<T>(column));
} public static bool ParseBoolean(this DataRow row, string columnName)
} {
if (!row.Table.Columns.Contains(columnName)) {
throw new KeyNotFoundException(string.Format("Column {0} was not found in DataRow.", columnName));
}
return row.Field<string>(row.Table.Columns[columnName]).ToBoolean();
}
public static IEnumerable<T> Values<T>(this DataColumn column)
{
return column.Table.AsEnumerable().Select(r => r.Field<T>(column));
}
}
} }
\ No newline at end of file
...@@ -292,7 +292,7 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration ...@@ -292,7 +292,7 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration
} }
[TestCase] [TestCase]
public void CompueShiftPolygonDeclarationTest() public void ComputeShiftPolygonDeclarationTest()
{ {
var engineFile = @"TestData\Components\40t_Long_Haul_Truck.veng"; var engineFile = @"TestData\Components\40t_Long_Haul_Truck.veng";
var gearboxFile = @"TestData\Components\40t_Long_Haul_Truck.vgbx"; var gearboxFile = @"TestData\Components\40t_Long_Haul_Truck.vgbx";
...@@ -372,9 +372,8 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration ...@@ -372,9 +372,8 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration
Assert.AreEqual(0, shiftPolygons.Last().Upshift.Count); Assert.AreEqual(0, shiftPolygons.Last().Upshift.Count);
} }
[TestCase] [TestCase]
public void CompueShiftPolygonATDeclarationTest() public void ComputeShiftPolygonATDeclarationTest()
{ {
var engineFile = @"TestData\Components\40t_Long_Haul_Truck.veng"; var engineFile = @"TestData\Components\40t_Long_Haul_Truck.veng";
var gearboxFile = @"TestData\Components\40t_Long_Haul_Truck.vgbx"; var gearboxFile = @"TestData\Components\40t_Long_Haul_Truck.vgbx";
...@@ -423,7 +422,7 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration ...@@ -423,7 +422,7 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration
} }
[TestCase] [TestCase]
public void CompueShiftPolygonDeclarationTestConfidentialEngine() public void ComputeShiftPolygonDeclarationTestConfidentialEngine()
{ {
//var engineFldFile = @"E:\QUAM\Downloads\EngineFLD\Map_375c_BB1390_modTUG_R49_375c_BB1386.vfld"; //var engineFldFile = @"E:\QUAM\Downloads\EngineFLD\Map_375c_BB1390_modTUG_R49_375c_BB1386.vfld";
var engineFldFile = @"E:\QUAM\tmp\scania_fullload_shiftpolygon-test.csv"; var engineFldFile = @"E:\QUAM\tmp\scania_fullload_shiftpolygon-test.csv";
...@@ -439,7 +438,6 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration ...@@ -439,7 +438,6 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration
var gearboxData = new JSONGearboxDataV6(JSONInputDataFactory.ReadFile(gearboxFile), gearboxFile); var gearboxData = new JSONGearboxDataV6(JSONInputDataFactory.ReadFile(gearboxFile), gearboxFile);
var engineData = new CombustionEngineData() { var engineData = new CombustionEngineData() {
IdleSpeed = 509.RPMtoRad(), IdleSpeed = 509.RPMtoRad(),
}; };
...@@ -453,7 +451,6 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration ...@@ -453,7 +451,6 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration
} }
engineData.FullLoadCurves = fullLoadCurves; engineData.FullLoadCurves = fullLoadCurves;
var shiftPolygons = new List<ShiftPolygon>(); var shiftPolygons = new List<ShiftPolygon>();
var downshiftTransformed = new List<List<Point>>(); var downshiftTransformed = new List<List<Point>>();
var downshiftOrig = new List<List<Point>>(); var downshiftOrig = new List<List<Point>>();
...@@ -508,10 +505,12 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration ...@@ -508,10 +505,12 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration
0.421, 4.18, 600), 0.421, 4.18, 600),
TestCase(@"class2_12t_Pmax_low\130kW_Diesel_example.vfld", @"class2_12t_Pmax_low\delivery_12t_example.vgbx", 0.421, TestCase(@"class2_12t_Pmax_low\130kW_Diesel_example.vfld", @"class2_12t_Pmax_low\delivery_12t_example.vgbx", 0.421,
4.18, 600), 4.18, 600),
TestCase(@"class5_40t_baseline\12L-324kW.vfld", @"class5_40t_baseline\tractor_12gear_example.vgbx", 0.421, 2.64, 600), TestCase(@"class5_40t_baseline\12L-324kW.vfld", @"class5_40t_baseline\tractor_12gear_example.vgbx", 0.421, 2.64,
600),
TestCase(@"class5_40t_iaxle_long\12L-324kW.vfld", @"class5_40t_iaxle_long\tractor_12gear_example.vgbx", 0.421, 2.31, TestCase(@"class5_40t_iaxle_long\12L-324kW.vfld", @"class5_40t_iaxle_long\tractor_12gear_example.vgbx", 0.421, 2.31,
600), 600),
TestCase(@"class5_40t_iaxle_short\12L-324kW.vfld", @"class5_40t_iaxle_short\tractor_12gear_example.vgbx", 0.421, 3.71, TestCase(@"class5_40t_iaxle_short\12L-324kW.vfld", @"class5_40t_iaxle_short\tractor_12gear_example.vgbx", 0.421,
3.71,
600), 600),
TestCase(@"class5_40t_Pmax_high\13-9-L-375kW.vfld", @"class5_40t_Pmax_high\tractor_12gear_example.vgbx", 0.421, TestCase(@"class5_40t_Pmax_high\13-9-L-375kW.vfld", @"class5_40t_Pmax_high\tractor_12gear_example.vgbx", 0.421,
2.64, 600), 2.64, 600),
...@@ -540,7 +539,6 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration ...@@ -540,7 +539,6 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration
} }
engineData.FullLoadCurves = fullLoadCurves; engineData.FullLoadCurves = fullLoadCurves;
var shiftPolygons = new List<ShiftPolygon>(); var shiftPolygons = new List<ShiftPolygon>();
var downshiftTransformed = new List<List<Point>>(); var downshiftTransformed = new List<List<Point>>();
var upshiftOrig = new List<List<Point>>(); var upshiftOrig = new List<List<Point>>();
...@@ -548,7 +546,7 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration ...@@ -548,7 +546,7 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration
shiftPolygons.Add( shiftPolygons.Add(
DeclarationData.Gearbox.ComputeShiftPolygon(gearboxData.Type, i, fullLoadCurves[(uint)(i + 1)], gearboxData.Gears, DeclarationData.Gearbox.ComputeShiftPolygon(gearboxData.Type, i, fullLoadCurves[(uint)(i + 1)], gearboxData.Gears,
engineData, axlegearRatio, rdyn.SI<Meter>()) engineData, axlegearRatio, rdyn.SI<Meter>())
); );
List<Point> tmp1, tmp2, tmp3; List<Point> tmp1, tmp2, tmp3;
ComputShiftPolygonPoints(i, fullLoadCurves[(uint)(i + 1)], gearboxData.Gears, ComputShiftPolygonPoints(i, fullLoadCurves[(uint)(i + 1)], gearboxData.Gears,
engineData, axlegearRatio, rdyn.SI<Meter>(), out tmp1, out tmp2, out tmp3); engineData, axlegearRatio, rdyn.SI<Meter>(), out tmp1, out tmp2, out tmp3);
...@@ -627,5 +625,53 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration ...@@ -627,5 +625,53 @@ namespace TUGraz.VectoCore.Tests.Models.Declaration
downshiftTransformed = new[] { p2p, p6p, p3pExt }.ToList(); downshiftTransformed = new[] { p2p, p6p, p3pExt }.ToList();
} }
/// <summary>
/// VECTO-517 Shiftpolygon is considered invalid
/// </summary>
[TestCase]
public void ShiftCurve_ShiftPolygon_Validation_Test()
{
var vgbs = new[] {
"-50,685,1537",
"550,685,1537",
"678,763,1537",
"1080,1008,2092",
"1200,1081,2092",
"1200,1081,2092",
"3000,1081,2092"
};
var shiftPolygon =
ShiftPolygonReader.Create(
VectoCSVFile.ReadStream(
InputDataHelper.InputDataAsStream("engine torque,downshift rpm [rpm],upshift rpm [rpm] ", vgbs)));
var results = shiftPolygon.Validate(ExecutionMode.Engineering, GearboxType.MT, false);
Assert.IsFalse(results.Any(), string.Join("\n", results.Select(r => r.ErrorMessage)));
}
[
TestCase(false, 650, 400),
TestCase(true, 400, 500),
TestCase(false, 900, 400),
TestCase(false, 1200, 400),
TestCase(true, 600, 900),
TestCase(false, 1000, 900),
TestCase(false, 1200, 900),
TestCase(true, 300, 1300),
TestCase(true, 900, 1300),
TestCase(false, 1200, 1250),
TestCase(false, 1200, 1600),
]
public void IsLeftOf_Test(bool result, double speed, double torque)
{
var segment = Tuple.Create(
new ShiftPolygon.ShiftPolygonEntry(550.SI<NewtonMeter>(), 685.RPMtoRad()),
new ShiftPolygon.ShiftPolygonEntry(1200.SI<NewtonMeter>(), 1080.RPMtoRad())
);
Assert.AreEqual(result, ShiftPolygon.IsLeftOf(speed.RPMtoRad(), torque.SI<NewtonMeter>(), segment));
}
} }
} }
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment