kerbaldyn 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +2 -0
- data/Gemfile +11 -0
- data/README.rdoc +57 -0
- data/Rakefile +23 -0
- data/kerbaldyn.gemspec +30 -0
- data/lib/kerbaldyn.rb +14 -0
- data/lib/kerbaldyn/body.rb +48 -0
- data/lib/kerbaldyn/constants.rb +12 -0
- data/lib/kerbaldyn/data.rb +56 -0
- data/lib/kerbaldyn/data/planet_data.json +249 -0
- data/lib/kerbaldyn/mixin.rb +2 -0
- data/lib/kerbaldyn/mixin/options_processor.rb +17 -0
- data/lib/kerbaldyn/mixin/parameter_attributes.rb +38 -0
- data/lib/kerbaldyn/orbit.rb +379 -0
- data/lib/kerbaldyn/orbital_maneuver.rb +6 -0
- data/lib/kerbaldyn/orbital_maneuver/base.rb +159 -0
- data/lib/kerbaldyn/orbital_maneuver/bielliptic.rb +61 -0
- data/lib/kerbaldyn/orbital_maneuver/burn_event.rb +57 -0
- data/lib/kerbaldyn/orbital_maneuver/hohmann.rb +48 -0
- data/lib/kerbaldyn/orbital_maneuver/inclination_change.rb +0 -0
- data/lib/kerbaldyn/part.rb +15 -0
- data/lib/kerbaldyn/part/base.rb +154 -0
- data/lib/kerbaldyn/part/fuel_tank.rb +11 -0
- data/lib/kerbaldyn/part/generic.rb +10 -0
- data/lib/kerbaldyn/part/liquid_fuel_engine.rb +69 -0
- data/lib/kerbaldyn/part/mixin.rb +1 -0
- data/lib/kerbaldyn/part/mixin/fuel_tank.rb +35 -0
- data/lib/kerbaldyn/part/rcs_fuel_tank.rb +10 -0
- data/lib/kerbaldyn/part/solid_rocket.rb +30 -0
- data/lib/kerbaldyn/part_library.rb +55 -0
- data/lib/kerbaldyn/planetoid.rb +214 -0
- data/lib/kerbaldyn/version.rb +13 -0
- data/spec/bielliptic_orbital_maneuver_spec.rb +60 -0
- data/spec/constants_spec.rb +9 -0
- data/spec/hohmann_orbital_maneuver_spec.rb +385 -0
- data/spec/options_processor_spec.rb +33 -0
- data/spec/orbit_spec.rb +357 -0
- data/spec/orbital_maneuver_base_spec.rb +74 -0
- data/spec/parameter_attributes_spec.rb +82 -0
- data/spec/part_library_spec.rb +21 -0
- data/spec/part_spec.rb +218 -0
- data/spec/planetoid_spec.rb +117 -0
- data/spec/spec_helper.rb +110 -0
- data/spec/support/parts/RCSFuelTank/part.cfg +42 -0
- data/spec/support/parts/fuelTank/part.cfg +48 -0
- data/spec/support/parts/liquidEngine1/part.cfg +60 -0
- data/spec/support/parts/liquidEngine2/part.cfg +64 -0
- data/spec/support/parts/solidBooster/part.cfg +67 -0
- data/spec/support/planet_test_data.json +340 -0
- metadata +95 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe KerbalDyn::Mixin::ParameterAttributes do
|
4
|
+
|
5
|
+
class ParameterAttributesTestClass
|
6
|
+
include KerbalDyn::Mixin::ParameterAttributes
|
7
|
+
|
8
|
+
def initialize(foo)
|
9
|
+
self.foo = foo
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_parameter :foo
|
13
|
+
alias_parameter :bar, :foo
|
14
|
+
|
15
|
+
attr_parameter :alpha, :beta, :gamma
|
16
|
+
end
|
17
|
+
|
18
|
+
describe 'attr_parameter' do
|
19
|
+
|
20
|
+
before(:each) do
|
21
|
+
@obj = ParameterAttributesTestClass.new(42)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should supply parameter getter' do
|
25
|
+
@obj.should respond_to(:foo)
|
26
|
+
@obj.foo.should == 42
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should supply parameter setter' do
|
30
|
+
@obj.should respond_to(:foo=)
|
31
|
+
@obj.foo = 88
|
32
|
+
@obj.foo.should == 88
|
33
|
+
end
|
34
|
+
|
35
|
+
[Integer, Float, Rational, Complex].each do |type|
|
36
|
+
it "should convert parameter of type #{type} as a float" do
|
37
|
+
value = Kernel.send(type.name.to_sym, 88)
|
38
|
+
value.should be_kind_of(type)
|
39
|
+
|
40
|
+
@obj.foo = value
|
41
|
+
|
42
|
+
@obj.foo.should be_kind_of(Float)
|
43
|
+
@obj.foo.should be_within_six_sigma_of(88.0)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should accept nil' do
|
48
|
+
@obj.foo = nil
|
49
|
+
@obj.foo.should == nil
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should accept multiple arguments' do
|
53
|
+
@obj.should respond_to :alpha
|
54
|
+
@obj.should respond_to :beta
|
55
|
+
@obj.should respond_to :gamma
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'alias_parameter' do
|
61
|
+
|
62
|
+
before(:each) do
|
63
|
+
@obj = ParameterAttributesTestClass.new(42)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should read aliased parameter value" do
|
67
|
+
@obj.should respond_to(:bar)
|
68
|
+
@obj.bar.should == @obj.foo
|
69
|
+
@obj.foo = 88
|
70
|
+
@obj.bar.should == @obj.foo
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should set aliased parameter value" do
|
74
|
+
@obj.should respond_to(:bar=)
|
75
|
+
@obj.bar = 88
|
76
|
+
@obj.bar.should == @obj.foo
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe KerbalDyn::PartLibrary do
|
4
|
+
|
5
|
+
it 'should be enumerable'
|
6
|
+
|
7
|
+
it 'should respond to length'
|
8
|
+
|
9
|
+
it 'should have convenience methods for making sub libraries by module'
|
10
|
+
|
11
|
+
it 'should export as JSON'
|
12
|
+
|
13
|
+
it 'should export as CSV'
|
14
|
+
|
15
|
+
describe 'parser' do
|
16
|
+
|
17
|
+
it 'should parse a directory of parts into a library'
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/spec/part_spec.rb
ADDED
@@ -0,0 +1,218 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe KerbalDyn::Part do
|
4
|
+
|
5
|
+
before(:all) do
|
6
|
+
@liquid_engine_attr = {"name"=>"liquidEngine", "module"=>"LiquidFuelEngine", "author"=>"Mrbrownce", "mesh"=>"model.mu", "scale"=>"0.1", "node_stack_top"=>"0.0, 7.21461, 0.0, 0.0, 1.0, 0.0", "node_stack_bottom"=>"0.0, -7.27403, 0.0, 0.0, 1.0, 0.0", "fx_exhaustFlame_blue"=>"0.0, -10.3, 0.0, 0.0, 1.0, 0.0, active", "fx_exhaustLight_blue"=>"0.0, -10.3, 0.0, 0.0, 0.0, 1.0, active", "fx_smokeTrail_light"=>"0.0, -10.3, 0.0, 0.0, 1.0, 0.0, active", "sound_vent_medium"=>"activate", "sound_rocket_hard"=>"active", "sound_vent_soft"=>"deactivate", "cost"=>"850", "category"=>"0", "subcategory"=>"0", "title"=>"LV-T30 Liquid Fuel Engine", "manufacturer"=>"Jebediah Kerman's Junkyard and Spaceship Parts Co.", "description"=>"Although criticized by some due to its not unsignificant use of so-called \"pieces found lying about\", the LV-T series has proven itself as a comparatively reliable engine. The T30 model boasts a failure ratio below the 50% mark. This has been considered a major improvement over previous models by engineers and LV-T enthusiasts.", "attachRules"=>"1,0,1,0,0", "mass"=>"1.25", "dragModelType"=>"default", "maximum_drag"=>"0.2", "minimum_drag"=>"0.2", "angularDrag"=>"2", "crashTolerance"=>"7", "maxTemp"=>"3600", "maxThrust"=>"215", "minThrust"=>"0", "heatProduction"=>"400", "Isp"=>"320", "vacIsp"=>"370"}
|
7
|
+
@liquid_engine2_attr = {"name"=>"liquidEngine2", "module"=>"LiquidFuelEngine", "author"=>"Mrbrownce", "mesh"=>"model.mu", "scale"=>"0.1", "node_stack_top"=>"0.0, 7.21461, 0.0, 0.0, 1.0, 0.0", "node_stack_bottom"=>"0.0, -5.74338, 0.0, 0.0, 1.0, 0.0", "fx_exhaustFlame_blue"=>"0.0, -5.74338, 0.0, 0.0, 1.0, 0.0, active", "fx_exhaustLight_blue"=>"0.0, -5.74338, 0.0, 0.0, 0.0, 1.0, active", "fx_smokeTrail_light"=>"0.0, -5.74338, 0.0, 0.0, 1.0, 0.0, active", "sound_vent_medium"=>"activate", "sound_rocket_hard"=>"active", "sound_vent_soft"=>"deactivate", "cost"=>"950", "category"=>"0", "subcategory"=>"0", "title"=>"LV-T45 Liquid Fuel Engine", "manufacturer"=>"Jebediah Kerman's Junkyard and Spaceship Parts Co.", "description"=>"The LV-T45 engine was considered another breakthrough in the LV-T series, due to its Thrust Vectoring feature. The LV-T45 can deflect its thrust to aid in craft control. All these added mechanics however, make for a slightly smaller and less powerful engine in comparison with earlier LV-T models.", "attachRules"=>"1,0,1,0,0", "mass"=>"1.5", "dragModelType"=>"default", "maximum_drag"=>"0.2", "minimum_drag"=>"0.2", "angularDrag"=>"2", "crashTolerance"=>"7", "maxTemp"=>"3600", "maxThrust"=>"200", "minThrust"=>"0", "heatProduction"=>"440", "fuelConsumption"=>"7", "Isp"=>"320", "vacIsp"=>"370", "thrustVectoringCapable"=>"True", "gimbalRange"=>"1.0"}
|
8
|
+
@fuel_tank_attr = {"name"=>"fuelTank", "module"=>"FuelTank", "author"=>"Mrbrownce", "mesh"=>"model.mu", "scale"=>"0.1", "node_stack_top"=>"0.0, 7.72552, 0.0, 0.0, 1.0, 0.0", "node_stack_bottom"=>"0.0, -7.3, 0.0, 0.0, 1.0, 0.0", "node_attach"=>"5.01, 0.0, 0.0, 1.0, 0.0, 0.0, 1", "cost"=>"850", "category"=>"0", "subcategory"=>"0", "title"=>"FL-T400 Fuel Tank", "manufacturer"=>"Jebediah Kerman's Junkyard and Spaceship Parts Co.", "description"=>"The FL series was received as a substantial upgrade over previous fuel containers used in the Space Program, generally due to its ability to keep the fuel unexploded more often than not. Fuel tanks are useless if there isn't a Liquid Engine attached under it. They can also be stacked with other fuel tanks to increase the amount of fuel for the engine below.", "attachRules"=>"1,1,1,1,0", "mass"=>"2.25", "dragModelType"=>"default", "maximum_drag"=>"0.2", "minimum_drag"=>"0.3", "angularDrag"=>"2", "crashTolerance"=>"6", "breakingForce"=>"50", "breakingTorque"=>"50", "maxTemp"=>"2900", "fuel"=>"400.0", "dryMass"=>"0.25", "fullExplosionPotential"=>"0.9", "emptyExplosionPotential"=>"0.1"}
|
9
|
+
@rcs_tank_attr = {"name"=>"RCSFuelTank", "module"=>"RCSFuelTank", "author"=>"Mrbrownce || HarvesteR", "mesh"=>"model.mu", "scale"=>"0.1", "node_stack_top"=>"0.0, 4.64624, 0.0, 0.0, 1.0, 0.0", "node_stack_bottom"=>"0.0, 0.23193, 0.0, 0.0, 1.0, 0.0", "cost"=>"800", "category"=>"0", "subcategory"=>"0", "title"=>"FL-R25 RCS Fuel Tank", "manufacturer"=>"Jebediah Kerman's Junkyard and Spaceship Parts Co.", "description"=>"These fuel tanks carry pressurized gas propellant for RCS thrusters. New advances in plumbing technology made it possible to route RCS lines to any point in the ship. So unlike liquid fuel tanks, RCS Fuel tanks can be placed anywhere.", "attachRules"=>"1,0,1,1,0", "mass"=>"0.5", "dragModelType"=>"default", "maximum_drag"=>"0.2", "minimum_drag"=>"0.2", "angularDrag"=>"2", "crashTolerance"=>"12", "maxTemp"=>"2900", "fuel"=>"200", "dryMass"=>"0.1", "fullExplosionPotential"=>"0.7", "emptyExplosionPotential"=>"0.1"}
|
10
|
+
@booster_attr = {"name"=>"solidBooster", "module"=>"SolidRocket", "author"=>"Il Carnefice", "mesh"=>"model.mu", "scale"=>"0.1", "node_stack_bottom"=>"0.0, -12.5127, 0.0, 0.0, 1.0, 0.0, 1", "node_stack_top"=>"0.0, 10.2547, 0.0, 0.0, 1.0, 0.0, 1", "node_attach"=>"0.0, 0.0, -5, 0.0, 0.0, 1.0, 1", "fx_exhaustFlame_yellow"=>"0.0, -11.2673, 0.0, 0.0, 1.0, 0.0, active", "fx_exhaustSparks_yellow"=>"0.0, -11.2673, 0.0, 0.0, 1.0, 0.0, active", "fx_smokeTrail_medium"=>"0.0, -11.2673, 0.0, 0.0, 1.0, 0.0, active", "sound_vent_medium"=>"activate", "sound_rocket_hard"=>"active", "sound_vent_soft"=>"deactivate", "cost"=>"450", "category"=>"0", "subcategory"=>"0", "title"=>"RT-10 Solid Fuel Booster", "description"=>"While considered by some to be little more than \"a trash bin full o' boom\", The RT-10 is used in many space programs, whenever the need to save cash is greater than the need to keep astronauts alive. Use with caution, though. Once lit, solid fuel motors cannot be put out until the fuel runs out.", "attachRules"=>"1,1,1,1,0", "mass"=>"1.8", "dragModelType"=>"default", "maximum_drag"=>"0.3", "minimum_drag"=>"0.2", "angularDrag"=>"2", "crashTolerance"=>"7", "maxTemp"=>"3600", "thrust"=>"250", "dryMass"=>"0.36", "heatProduction"=>"550", "fuelConsumption"=>"4", "internalFuel"=>"100", "fullExplosionPotential"=>"0.8", "emptyExplosionPotential"=>"0.1", "thrustCenter"=>"0, -0.5, 0", "thrustVector"=>"0, 1, 0"}
|
11
|
+
end
|
12
|
+
|
13
|
+
describe KerbalDyn::Part::Base do
|
14
|
+
|
15
|
+
describe 'properties' do
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
@attributes = {'name' => 'fooThruster', 'module' => 'LiquidFuelEngine', 'category' => '0'}
|
19
|
+
@part = KerbalDyn::Part::Base.new(@attributes)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Rather than test every single property, we test those that have non-trivial
|
23
|
+
# accessors.
|
24
|
+
|
25
|
+
it 'should provide string and symbol access to attributes' do
|
26
|
+
@part['name'].should == @attributes['name']
|
27
|
+
@part[:name].should == @attributes['name']
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should provide category name' do
|
31
|
+
@part.category.should == KerbalDyn::Part::CATEGORIES['Propulsion']
|
32
|
+
@part.category_name.should == 'Propulsion'
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should provide module class' do
|
36
|
+
@part.module_class.should == KerbalDyn::Part::LiquidFuelEngine
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should copy the attributes passed to it' do
|
40
|
+
attributes = @attributes.dup
|
41
|
+
part = KerbalDyn::Part::Base.new(attributes)
|
42
|
+
|
43
|
+
part.attributes.object_id.should_not == attributes.object_id
|
44
|
+
|
45
|
+
part['name'].should == 'fooThruster'
|
46
|
+
attributes['name'] == 'barThruster'
|
47
|
+
part['name'].should_not == 'barThruster'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should convert to hash that is not the attributes hash itself' do
|
51
|
+
hash = @part.to_hash
|
52
|
+
|
53
|
+
hash.should == @part.attributes
|
54
|
+
hash.object_id.should_not == @part.attributes.object_id
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should export as JSON' do
|
58
|
+
part_json = @part.to_json
|
59
|
+
attributes_json = @part.to_hash.to_json
|
60
|
+
|
61
|
+
part_json.should == attributes_json
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should export as CSV' # This will need a predefined set of properties to output.
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'loader' do
|
69
|
+
|
70
|
+
before(:all) do
|
71
|
+
@parts_directory = File.join(File.dirname(__FILE__), 'support', 'parts')
|
72
|
+
@liquid_engine_part_dir = File.join(@parts_directory, 'liquidEngine1')
|
73
|
+
@part_dir = @liquid_engine_part_dir
|
74
|
+
end
|
75
|
+
|
76
|
+
before(:each) do
|
77
|
+
@part = KerbalDyn::Part::Base.load_part(@part_dir)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should load parts from part directories' do
|
81
|
+
@part.should be_kind_of(KerbalDyn::Part::Base)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should instantiate as the module attribute class' do
|
85
|
+
@part.class.name.should =~ Regexp.new( 'KerbalDyn::Part::' + @part[:module] + "$")
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'should default to instantiating as generic'
|
89
|
+
|
90
|
+
it 'should return nil if no part was found' do
|
91
|
+
dir_path = File.join(@parts_directory, 'tardis') # Doesn't exist.
|
92
|
+
part = KerbalDyn::Part::Base.load_part(dir_path)
|
93
|
+
part.should == nil
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should log parse errors'
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
describe KerbalDyn::Part::Generic do
|
103
|
+
|
104
|
+
it 'should be a subclass of Base' do
|
105
|
+
KerbalDyn::Part::Generic < KerbalDyn::Part::Base
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
describe KerbalDyn::Part::FuelTank do
|
111
|
+
|
112
|
+
before(:each) do
|
113
|
+
@fuel_tank = KerbalDyn::Part::FuelTank.new(@fuel_tank_attr)
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should calculate capacity in m^3' do
|
117
|
+
@fuel_tank.capacity.should be_within_six_sigma_of(0.4)
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should calculate fuel mass' do
|
121
|
+
@fuel_tank.fuel_mass.should be_within_six_sigma_of(2.0)
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should calculate fuel density in kg/m^3' do
|
125
|
+
@fuel_tank.fuel_density.should be_within_six_sigma_of(5.0)
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
describe KerbalDyn::Part::RCSFuelTank do
|
131
|
+
|
132
|
+
before(:each) do
|
133
|
+
@rcs_tank = KerbalDyn::Part::FuelTank.new(@rcs_tank_attr)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should calculate capacity in m^3' do
|
137
|
+
@rcs_tank.capacity.should be_within_six_sigma_of(0.2)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should calculate fuel mass' do
|
141
|
+
@rcs_tank.fuel_mass.should be_within_six_sigma_of(0.4)
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'should calculate fuel density in kg/m^3' do
|
145
|
+
@rcs_tank.fuel_density.should be_within_six_sigma_of(2.0)
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
describe KerbalDyn::Part::LiquidFuelEngine do
|
151
|
+
|
152
|
+
before(:each) do
|
153
|
+
@liquid_engine = KerbalDyn::Part::LiquidFuelEngine.new(@liquid_engine_attr)
|
154
|
+
@liquid_engine2 = KerbalDyn::Part::LiquidFuelEngine.new(@liquid_engine2_attr)
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should give Isp at sealevel' do
|
158
|
+
measured = 320.7
|
159
|
+
@liquid_engine.isp.should be_within_three_sigma_of(measured)
|
160
|
+
end
|
161
|
+
|
162
|
+
it 'should give vacuum Isp' do
|
163
|
+
specified = 370.0
|
164
|
+
@liquid_engine.vac_isp.should be_within_six_sigma_of(specified)
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should calculate mass flow rate' do
|
168
|
+
expected = (215.0/320.0) / 9.8066 # Not sure what the 5th digit of g should be.
|
169
|
+
@liquid_engine.mass_flow_rate.should be_within_three_sigma_of(expected)
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'should calculate vacuum mass flow rate' do
|
173
|
+
expected = (215.0/370.0) / 9.8066 # Not sure what hte 5th digit of g should be.
|
174
|
+
@liquid_engine.vac_mass_flow_rate.should be_within_three_sigma_of(expected)
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'should calculate fuel consumption in m^3/s' do
|
178
|
+
fuel_tank = KerbalDyn::Part::FuelTank.new(@fuel_tank_attr)
|
179
|
+
measured = 13.7 # in liters/second
|
180
|
+
@liquid_engine.fuel_consumption(fuel_tank).should be_within_three_sigma_of(measured/1000.0)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'should calculate vacuum fuel consumption in m^3/s' do
|
184
|
+
fuel_tank = KerbalDyn::Part::FuelTank.new(@fuel_tank_attr)
|
185
|
+
@liquid_engine.vac_fuel_consumption(fuel_tank).should be_within_two_sigma_of(0.0119)
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
describe KerbalDyn::Part::SolidRocket do
|
191
|
+
|
192
|
+
before(:each) do
|
193
|
+
@booster = KerbalDyn::Part::SolidRocket.new(@booster_attr)
|
194
|
+
end
|
195
|
+
|
196
|
+
it 'should calculate capacity in m^3' do
|
197
|
+
@booster.capacity.should be_within_six_sigma_of(0.1)
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'should calculate fuel mass' do
|
201
|
+
@booster.fuel_mass.should be_within_six_sigma_of(1.44)
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'should calculate fuel density in kg/m^3' do
|
205
|
+
@booster.fuel_density.should be_within_six_sigma_of(14.4)
|
206
|
+
end
|
207
|
+
|
208
|
+
it 'should calculate fuel consumption in m^3/s' do
|
209
|
+
@booster.fuel_consumption.should be_within_six_sigma_of(0.004)
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'should calculate burn time' do
|
213
|
+
@booster.burn_time.should be_within_six_sigma_of(25.0)
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe KerbalDyn::Planetoid do
|
4
|
+
|
5
|
+
describe 'Properties' do
|
6
|
+
before(:each, &BeforeFactory.earth)
|
7
|
+
|
8
|
+
it "should have a name" do
|
9
|
+
@planetoid.name.should == @name
|
10
|
+
end
|
11
|
+
|
12
|
+
[
|
13
|
+
:mass,
|
14
|
+
:radius,
|
15
|
+
:rotational_period,
|
16
|
+
:gravitational_parameter,
|
17
|
+
:surface_gravity,
|
18
|
+
:volume,
|
19
|
+
:density,
|
20
|
+
:angular_velocity,
|
21
|
+
:equitorial_velocity,
|
22
|
+
:escape_velocity,
|
23
|
+
].each do |method|
|
24
|
+
it "should calculate #{method}" do
|
25
|
+
value = self.instance_variable_get("@#{method}")
|
26
|
+
# Two sigma let's rough calculations (like those that assume a sphere instead of a spheroid) pass.
|
27
|
+
@planetoid.send(method).should be_within_two_sigma_of(value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
[
|
32
|
+
[:gravitational_acceleration, :surface_gravity, :radius],
|
33
|
+
].each do |method, expected_var, *args_vars|
|
34
|
+
it "should calculate #{method}" do
|
35
|
+
expected = self.instance_variable_get("@#{expected_var}")
|
36
|
+
args = args_vars.map {|var| self.instance_variable_get("@#{var}")}
|
37
|
+
@planetoid.send(method, *args).should be_within_two_sigma_of(expected)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
describe 'Planetoid Library' do
|
45
|
+
|
46
|
+
describe 'Kerbol' do
|
47
|
+
|
48
|
+
before(:all) do
|
49
|
+
@planet_fact = KerbalDyn::Planetoid.kerbol
|
50
|
+
@expected_name = 'Kerbol'
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should be available via a factory method' do
|
54
|
+
@planet_fact.should be_kind_of(KerbalDyn::Planetoid)
|
55
|
+
@planet_fact.name.should == @expected_name
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should be a memoized' do
|
59
|
+
# We fetch the constant and the factory method value twice, to be sure they are all the same.
|
60
|
+
unique_ids = [@planet_fact, @planet_fact].map {|obj| obj.object_id}.uniq
|
61
|
+
unique_ids.length.should == 1
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'should be frozen' do
|
65
|
+
@planet_fact.should be_frozen
|
66
|
+
end
|
67
|
+
|
68
|
+
{
|
69
|
+
:radius => 261600000.0,
|
70
|
+
:mass => 1.756830203555361e28,
|
71
|
+
:surface_gravity => 17.13071282744409,
|
72
|
+
:gravitational_parameter => 1.172332794832492300e18,
|
73
|
+
:escape_velocity => 94672.00722134684
|
74
|
+
}.each do |param, value|
|
75
|
+
it "should have a #{param} of #{value}" do
|
76
|
+
@planet_fact.send(param).should be_within_six_sigma_of(value)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
PLANET_TEST_DATA.each do |planet_key, test_data|
|
83
|
+
data = test_data[:planetoid]
|
84
|
+
|
85
|
+
describe "#{data[:name]}" do
|
86
|
+
|
87
|
+
before(:all) do
|
88
|
+
@planetoid = KerbalDyn::Planetoid.send(planet_key)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should be memoized' do
|
92
|
+
unique_ids = [@planetoid, KerbalDyn::Planetoid.send(planet_key)].map {|obj| obj.object_id}.uniq
|
93
|
+
unique_ids.length.should == 1
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should be frozen' do
|
97
|
+
@planetoid.should be_frozen
|
98
|
+
end
|
99
|
+
|
100
|
+
data.each do |property, expected_value|
|
101
|
+
it "should have #{property} of #{expected_value}" do
|
102
|
+
case expected_value
|
103
|
+
when Float
|
104
|
+
@planetoid.send(property).should be_within_six_sigma_of(expected_value)
|
105
|
+
else
|
106
|
+
@planetoid.send(property).should == expected_value
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
Bundler.require(:test)
|
4
|
+
|
5
|
+
require 'kerbaldyn'
|
6
|
+
|
7
|
+
|
8
|
+
require 'json'
|
9
|
+
require 'pathname'
|
10
|
+
planet_test_data_file = Pathname.new(__FILE__).dirname + 'support' + 'planet_test_data.json'
|
11
|
+
PLANET_TEST_DATA = JSON.parse( planet_test_data_file.read, :symbolize_names => true )
|
12
|
+
|
13
|
+
|
14
|
+
RSpec::Matchers.define :be_within_error do |err|
|
15
|
+
chain :of do |expected|
|
16
|
+
@expected = expected
|
17
|
+
end
|
18
|
+
|
19
|
+
match do |actual|
|
20
|
+
actual.should be_within( (err*@expected).abs ).of(@expected)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
{:two => 21.977895, :three => 370.398, :four => 15787.0, :five => 1744278.0, :six => 506797346}.each do |level, frac|
|
25
|
+
err = 1.0 / frac.to_f
|
26
|
+
RSpec::Matchers.define "be_within_#{level}_sigma_of".to_sym do |expected|
|
27
|
+
match do |actual|
|
28
|
+
actual.should be_within_error(err).of(expected)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class BeforeFactory
|
34
|
+
|
35
|
+
# I looked up real values off of tables for the Earth as test data where possible.
|
36
|
+
def self.earth
|
37
|
+
return Proc.new do
|
38
|
+
@name = 'Earth'
|
39
|
+
@mass = 5.9736e24
|
40
|
+
@radius = 6378.1e3
|
41
|
+
@rotational_period = 86164.091
|
42
|
+
@angular_velocity = 7.2921150e-5
|
43
|
+
@volume = 1.08321e21
|
44
|
+
@density = 5515.0
|
45
|
+
@gravitational_parameter = 398600.4418e9 # 398600.4418e9 is on some tables, but other tables vary starting in the 4th digit!
|
46
|
+
@surface_gravity = 9.780327
|
47
|
+
@escape_velocity = 11.186e3
|
48
|
+
@equitorial_velocity = 465.1
|
49
|
+
|
50
|
+
@options = {
|
51
|
+
:mass => @mass,
|
52
|
+
:radius => @radius,
|
53
|
+
:rotational_period => @rotational_period
|
54
|
+
}
|
55
|
+
@planetoid = KerbalDyn::Planetoid.new(@name, @options)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# I looked up values from tables where possible.
|
60
|
+
def self.earth_geostationary_orbit
|
61
|
+
return Proc.new do
|
62
|
+
@geostationary_orbit_radius = 42164e3
|
63
|
+
@geostationary_orbit_altitude = 35786e3
|
64
|
+
@geostationary_orbit_angular_velocity = 7.2921e-5
|
65
|
+
@geostationary_orbit_velocity = 3074.6
|
66
|
+
@orbital_radius = @geostationary_orbit_radius
|
67
|
+
@orbital_period = @rotational_period
|
68
|
+
@orbital_velocity = @geostationary_orbit_velocity
|
69
|
+
@orbital_specific_kinetic_energy = 4726582.58 # Computed
|
70
|
+
@orbital_specific_potential_energy = -9453572.75875 # Computed
|
71
|
+
@orbital_specific_angular_momentum = 129640229204 # Computed using an alternative formula from a different source.
|
72
|
+
@orbit = KerbalDyn::Orbit.new(@planetoid, :radius => @orbital_radius)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# A third party transfer to geosynchronous orbit from 300km alt example that
|
77
|
+
# can be used for the basis of elliptical orbit tests.
|
78
|
+
def self.geostationary_transfer_orbit
|
79
|
+
return Proc.new do
|
80
|
+
@periapsis = 6678e3
|
81
|
+
@apoapsis = 42164e3
|
82
|
+
@periapsis_velocity = 10150
|
83
|
+
@apoapsis_velocity = 1610
|
84
|
+
@eccentricity = 0.72655
|
85
|
+
@semimajor_axis = 24421e3
|
86
|
+
@orbit = KerbalDyn::Orbit.new(@planetoid, :periapsis => @periapsis, :apoapsis => @apoapsis)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# A pre-calculated and verified parabloic escape orbit from earth.
|
91
|
+
def self.earth_escape_orbit
|
92
|
+
return Proc.new do
|
93
|
+
@periapsis = 6678e3
|
94
|
+
# I used the calculated gavitational parameter for better precision,
|
95
|
+
# since this is a "critical" orbit between elliptical and hyperbolc orbits.
|
96
|
+
@escape_velocity = Math.sqrt( 2.0 * @planetoid.gravitational_parameter / @periapsis )
|
97
|
+
@orbit = KerbalDyn::Orbit.new(@planetoid, :periapsis => @periapsis, :periapsis_velocity => @escape_velocity)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.earth_hyperbolic_orbit
|
102
|
+
return Proc.new do
|
103
|
+
@periapsis = 6678e3
|
104
|
+
@periapsis_velocity = 20000
|
105
|
+
@eccentricity = 5.701 #Precomputed
|
106
|
+
@orbit = KerbalDyn::Orbit.new(@planetoid, :periapsis => @periapsis, :periapsis_velocity => @periapsis_velocity)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|