KontrolSystem2 icon indicating copy to clipboard operation
KontrolSystem2 copied to clipboard

Request for bunch of Quality Of Life data

Open lefouvert opened this issue 1 year ago • 50 comments

version 0.5.3.4 (Ckan) gessed tag : Question, Enhancement

Hi ! Maybe I didn't be able to found thoses, in that case, «My bad», but I miss some acces to definitions datas.

Fairing (feels important to me)

There is 2 kind of fairing : the conveniences ones, and the wanted ones. I mean, when you put a stage under an engine, this one is covered by a fairing. It's thoses I call the conveniences ones. And there is obviously the fairing you put to protect your payload, have better aerodynamics etc aka the wanted ones.

Since vessel.parts.filter(fn(p) -> p.fairing.defined) is not able to distinguish them, I would like to know if there is an other way I didn't think about. At first, I was thinking «Nah, easy, I collect fairings, I substract engines, and i'm good to go» BUT ! Because there is a 'but', obviously, engines are not the only ones to have «conveniences» fairings. Heatshields too. And structural hollow tubes. And probably some others parts I didn't think about. And all thoses delicious parts don't have any modules to identify them :) As part.is_fairing is probably an equivalent of part.fairing.defined, his behavior is unsurprizingly the same. I'm not feeling good at the idea to look at the name of a part to identify it. I think it's not reliable. If I didn't miss a way, maybe a part.fairing.value.is_jettisonable could be a good idea (since I wasn't able to jettison «convenience» fairing)

Solar panels (feels important to me)

I have a nice piece of code which is able to give the one of best vectors to expose to the sun. But in order to be able to do it, I needs to know max output of each solar panels. Maybe I miss it, but I haven't be able to found it. I had to write this kind of things :

pub const SolarPanel: (Name: string, MaxFlow: float)[] = [
    (Name: "solararray_0v_flat", MaxFlow: 0.145209029316902), // OX-STAT
    (Name: "solararray_1v_1x6", MaxFlow: 0.968068957328796), // OX-4L
    (Name: "solararray_1v_3x2", MaxFlow: 0.968069076538086), // OX-4W
    (Name: "solararray_1v_shielded_1x6", MaxFlow: 0.968068957328796), // SP-4L
    (Name: "solararray_1v_shielded_3x2", MaxFlow: 0.968069076538086), // SP-4W
    (Name: "solararray_1v_flat", MaxFlow: 1.21007525920868), // OX-STAT-XL
    (Name: "solararray_1v_circular", MaxFlow: 3.3882417678833), // OX-10C
    (Name: "solararray_1v_circular_shielded", MaxFlow: 3.3882417678833), // SP-10C
    (Name: "solararray_2v_accordion", MaxFlow: 11.6168279647827), // SP-XL 'Gigantor'
    (Name: "solararray_3v_large", MaxFlow: 33.8824119567871), // SP-XXL 'Colosse'
]

As said, I don't think it's a reliable way to gather data.

Resources / Engines (seems to be a good idea)

Did I miss a way to connect an engine with the fuel it burn ?

I wrote this :

pub type ResourceConst = int

pub const Resource: (MP: ResourceConst, SF: ResourceConst, Air: ResourceConst, H: ResourceConst, CH4: ResourceConst, Ox: ResourceConst, EC: ResourceConst, Xe: ResourceConst, Ab: ResourceConst) = (
    MP: 1,  // "ME"->"MP" Monopropellant
    SF: 2,  // "ES"->"SF" Solid Fuel
    Air: 3, // "Air"->"Air" Air
    H: 6,   // "H"->"H" Helium
    CH4: 7, // "CH4"->"CH4" Methane (Liquid Fuel)
    Ox: 8,  // "Ox"->"Ox" Oxygen
    EC: 12, // "CE"->"EC" Electrical Charge
    Xe: 13, // "Xe"->"Xe" Xenon
    Ab: 15  // "Ab"->"Ab" Ablator
)

pub const EngineFuel: (Fuel: int, Engine: string)[] = [
    (Fuel: Resource.MP, Engine: "MonoProp"), // "ME"->"MP" Monopropellant
    (Fuel: Resource.SF, Engine: "SolidBooster"), // "ES"->"SF" Solid Fuel
    (Fuel: Resource.Air, Engine: "Turbine"), // "Air"->"Air" Air
    (Fuel: Resource.H, Engine: "Nuclear"), // "H"->"H" Helium
    (Fuel: Resource.CH4, Engine: "Methalox"), // "CH4"->"CH4" Methane (Liquid Fuel)
    (Fuel: Resource.CH4, Engine: "Turbine"), // "CH4"->"LF" Methane (Liquid Fuel)
    (Fuel: Resource.Ox, Engine: "Methalox"), // "Ox"->"Ox" Oxygen
    (Fuel: Resource.EC, Engine: "Electric"), // "CE"->"EC" Electrical Charge
    (Fuel: Resource.Xe, Engine: "Electric") // "Xe"->"Xe" Xenon
]

which is far more reliable than looking at part's name. It connect part.engine_module.value.current_engine_mode.engine_type to a part.resources.list.map(fn(r) -> r.resource.id). This is way less an issue than others topics, but just in case I miss something... :). But I don't think it's a bad idea to have a way to know what input/output each part are able to. For example, most Liquid Fueled engines have Oxygen and Methan as Input, and have Electric charges as output. Propellant Tanks have their propelant as output Solar panels have Electric charges as output etc

The cherry on the cake would be able to ignite individualy each engine, especially since part.engine_module.value.independent_throttle have appeared ^^

Heatshield (feels accessory)

Maybe thoses parts would have benefits to have their very own module, since they can be deployed, they have resources (Ablator), fairing, etc It would be a shame to inadvertently deploy one of those (Yes, I look at you, inflatable heatshield) while expecting deploy antennaes, solarpanels, cargoholds etc :) At the time, I'm able to filter them on the resources the part hold. If their is 'Ablator' in it, it's a heatshield.

Edit : typo, grammar etc

lefouvert avatar Feb 29 '24 16:02 lefouvert

If I may add:

ENGINES: access to engine's thrust limiter (R/W). WARP: it's possible to get current_rate and current_index of the actual warp. Is it possible to change them?

PhilouDS avatar Feb 29 '24 17:02 PhilouDS

@lefouvert I don't understand how Resource and EngineFuel work. What are the numbers next to the resources?

PhilouDS avatar Feb 29 '24 17:02 PhilouDS

@PhilouDS The numbers in Resource are the ingame ids of the resources. When you look at a part's resources, you may access to this id part.resources.list.map(fn(r) -> r.resource.id) ResourceContainer ResourceData ResourceDefinition Displayed names are more user friendly, but id's are more efficient to link datas.

EngineFuel is just an array that describe which engine burn which fuel.

So if I run a «LV-1 'Ant'» engine, I'm able to know it's a Methalox engine type part.engine_module.value.current_engine_mode.engine_type ModuleEngine EngineMode EngineType which will burn CH4 and Ox. I just have to search for parts inside the decouple stage I consider thoses which have CH4 and Ox.

With thoses two datas, you can know the mass a part will lost, and deduce lot of things as deltaV, isp, burntime etc. Kontrol System give us access to most of thoses datas, but I gess they are retrieved directly from the game, which implies some are missing. image This nice little thing is able to achieve a circular orbit around Kerbin, at any inclination. However KSP2 and Kontrol System vessel.delta_v.stages[].get_deltav(DeltaVSituation.Vaccum) are undivided : the 4 firsts stages aren't able to deliver any thrust, and don't provide any delta V. And I can garantee you that LV-1 'Ant' didn't lift it in the sky. image

lefouvert avatar Feb 29 '24 21:02 lefouvert

Thank you for all those explanations. Nice log in the Console. Too bad we can't save that in a txt file... yet ;-) In the BAV, I've created an UI and I compute myself the effective velocity of a stage to calculate then the delta-v. Maybe, I could look deeper of what you did to improve that.

At least, my stage info window (at the bottom) is better than the one from MicroEngineer (which only uses the numbers from KSP).

image

PhilouDS avatar Feb 29 '24 23:02 PhilouDS

@untoldwind I see you ^^ Thoses things_array.reduce(<Foobar>[], fn(flat, things) -> flat + things.filter(fn(t) -> t.defined)) seems to had given you some inspiration :) Thank you very much. For that and all.

lefouvert avatar Mar 01 '24 19:03 lefouvert

That are a lot of wishes and unluckily I messed my games-saves I use for testing. So there is just a pre-release for now: https://github.com/untoldwind/KontrolSystem2/releases/tag/v0.5.3.5 Updated documentation: https://kontrolsystem2.readthedocs.io/en/prerelease/

From top to bottom (mostly):

  • I added a .filter_map to array, which is similar to .map but the function should return an Option and only the defined value end up in the result.

    • So to get a list of all the solar_panels one can now just do vessel.parts.filter_map(fn(part) -> part.solar_panel)
    • Similarly all the other things like fairing/air intakes etc.
  • Also added a .flat_map, in which the function returns an array, that as then concatenated in result.

    • E.g. to get all the resources of a vessel: vessel.parts.flat_map(fn(part) -> part.resources.list)
  • As for the fairing: I checked the current implementation, part.fairing seems to be working as intended. Maybe you have a cargo-bay instead, which can be found and controlled via part.deployable?

  • As for solar panels: The energy-flow is actually a bit more complicated:

    • There is now a .base_flow_rate and .efficiency_multiplier which seem to be constants of the model
    • Additionally there is the .star_energy_scale which changes with the distance to the star (and probably also depends on the type of star once we get the interstellar patch)
    • The .max_flow is a shortcut for base_flow_rate * star_energy_scale * efficiency_multiplier
    • And eventually: .energy_flow is the current energy flow which is a fraction of .max_flow depending on sun-exposure (and angle I guess)
  • As for the propellant/resource information of the engine: Again a bit more complicated

    • There is now a .current_propellant which is the currently used propellant depending on the engine mode
    • Additionally .propellants returns all the potential propellants in the same order as .engine_modes (if the engine only has one mode there should be only one element)
    • Unluckily it gets even more complicated: The propellant might be a mixture different resources.
    • E.g. most engines will have .current_propellant.name == "Methalox", which is a mix of two resources
    • In these cases .current_propellant.is_recipe is true and .current_propellant.recipe_ingredients will contain a list of all the ingredients and their ratio.
  • The heatshield has been added with some basic fields: is_deployed is_ablating is_ablator_exhausted ablator_ratio

    • ... maybe this can be a bit more detailed
  • As for the thrust limiter: This should now be tweakable via engine.thrust_limiter

  • As for warp: Some basic information should have be already available in the ksp::game::warp module: https://kontrolsystem2.readthedocs.io/en/prerelease/reference/ksp/game_warp.html

    • At the moment this is read-only. I will check if there is an easy way to make tweakable as well

untoldwind avatar Mar 01 '24 20:03 untoldwind

Undoubtly, you may say me 'no' at anytime, if it's bad ideas or too much work. Besides, I'm blown away by the speed you implement all thoses ! From top to bottom ;) :

  • .filter_map & flat_map smell like candies. I'll have to re-read all my code to add this, it will be way more readable :)
  • Fairings : I'm not saying they don't work as intended. They absolutly work as intended. I was just seeking an idea to differenciate the "explicit" fairing as 'AEFF000' part or 'AEFF375' part, and the "implicit" fairing, as the one which cover an engine or a heatshield. issue130 I have a .check_fairing() method which is unable to see any difference between both, and after having successfully jettisoned the AEFF125, try desperately to jettison the engine fairing. In vain, obviously, since it will be automaticly jettisoned at the staging, not before, not after.
  • solar panels : I understand why you had to take care of all thoses parameters. I must admit it was an untold wish to have access to all of them. I'm fulfilled. I had some testings on solar panels, so I can answer you : Yes, .energy_flow is totaly dependent of the angle :)
  • Engines' propellants : as solar panels, your implementation is above expectations. Does .recipe_ingredients contains a list of 1 ingredient if it's not a recip, (for monoprop, for example) ?
  • Heatshield : at the time, I don't see more which could be usefull.
  • thrust limiter : Oh that's a cheating parameter x). Solid Booster powered launcher with in flight variable thrust will appear soon. I can garantee it ^^. I'll try for fun, but not in main code base.
  • Warp : PhilouDS oriented gift. At the time, I do not have any needs on this topic, since I do not have ended my ship.park() method which is , for me, a mandatory to warp. I will not let my vessel deplete all EC while time warping because of bad sun exposure :)

Can I say enough 'thank you' to illustrate how much I'm happy with all thoses addings ?

Edit typo, grammar etc as usual

lefouvert avatar Mar 01 '24 21:03 lefouvert

Ah ok, the engine fairing was not on my radar. Quick workaround would be to filter out the engines before using .filter_map. E.g.

vessel.parts.filter(fn(part) -> !part.is_engine).fitler_map(fn(part) -> part.fairing)

But I understand that this is somewhat confusing. I'll probably split the API in .fairing and .engine_fairing

As for the the propellants: If .is_recipe is false than the propellant is just the resource that is used, i.e. .receipe_ingredients should be empty. Otherwise this would be just a reference to self.

untoldwind avatar Mar 02 '24 13:03 untoldwind

As said, I had think about excluding engine parts, howerver, this behavior is also true for heatshield (nice we can recognize them and exclude them now ^^) AND at least thoses parts :

  • Structure/MountPlate/MEM-XXX
  • Structure/Tube/TUUB-XXX And maybe some other I could had miss.

lefouvert avatar Mar 02 '24 15:03 lefouvert

Created a new pre-release: https://github.com/untoldwind/KontrolSystem2/releases/tag/v0.5.3.6

  • part.fairing should now only cover fairings
    • EDIT: Sorry saw that last comment too late, only engines are excluded. I will try to find a stable way to only get "real" fairings.
  • For engines: part.engine.fairing or part.engine.has_fairing
  • To get cargo bay (which are deployables) there is now also a part.is_cargo_bay
    • Note: There is no corresponding part.cargo_bay because cargo bays do not really have any interesting values to tweak that are not covered with part.deployable

As for the time-warping:

  • The ksp::game::warp module has been extended: https://kontrolsystem2.readthedocs.io/en/prerelease/reference/ksp/game_warp.html
  • In particular there is a set_warp_index(int) function.
    • Note: This can not be used in a sync function ... I encountered some very strange behavior when not waiting for a next game update cycle. Playing with time has to be done with care, H.G. Wells wrote a book about it.

untoldwind avatar Mar 02 '24 17:03 untoldwind

Hopefully this is the one: https://github.com/untoldwind/KontrolSystem2/releases/tag/v0.5.3.7

I added part.part_category and .fairing should now only contain fairings in the Payload category

untoldwind avatar Mar 02 '24 18:03 untoldwind

Just did few testings : Notice deployable.value.deploy_state became a constant instead of a string. Nice touch ^^ Legitim fairings respond positively to .is_cargo_bay. Will test others aspects later, but it smell good.

lefouvert avatar Mar 03 '24 03:03 lefouvert

Little engine test. With is_recipe false, .recipe_ingredients is not even instancied (Not really surprizing). Out of curiosity, why .recipe_ingredientsis not an Option<ResourceReceipeIngredient[]> ? (Just to get a better understanding ! )

lefouvert avatar Mar 03 '24 12:03 lefouvert

Yes good point that should be an option.

untoldwind avatar Mar 03 '24 14:03 untoldwind

I merge it all into the 0.5.4.0 release. .recipe_ingredients is now Option<ResourceReceipeIngredient[]> as suggested

untoldwind avatar Mar 03 '24 15:03 untoldwind

Sorry, I was a bit busy this week-end, so I can't look at all I was expecting on KSP2 & KS2. Browing datas and engines, I was thinking about propellants. EngineMode govern which propelant is used, this is why an array is needed for part.engine_module.propellants. After had read your source code (and what I assume to be KSP2 .dll api's files like Data_Engine.cs), I assume index of part.engine_module.propellants match index part.engine_module.engine_modes. Would it make sense that propellants be a property of EngineMode (not array, therefore, but keeping the notion of recipe and the recipe array) rather than ModuleEngine? It's just an idea to talk about.

lefouvert avatar Mar 04 '24 10:03 lefouvert

I just noticed that the underlying structure of EngineMode also has a propellant definition, have to check if that is in sync with the the one on the engine though.

untoldwind avatar Mar 04 '24 17:03 untoldwind

I saw you added some information for engines and solar panels in the VAB too :) Is it possible to have access to the Electricity consumption of any part inside the VAB (to compute the total consumption)?

PhilouDS avatar Mar 04 '24 17:03 PhilouDS

@PhilouDS I just take a look at PartComponent.cs (little 2k lines file) which seems be a portion of the API. It don't seems to be an easy task. Apparently, all goes through something like a PartResourceFlowRequestBroker which don't seems to have a direct access thru a public method. I didn't explore enough to be fluent into thoses files, but seems to be a chore. Maybe I'm wrong, Untoldwind should be able to correct my assessments.

lefouvert avatar Mar 04 '24 19:03 lefouvert

Looks like all the Data_* structures (e.g. Data_Command, Data_Transmitter etc.) have either resource settings or requiredResources. At first glance this seems to contain ElectricalCharge as a resource requirement for certain modules.

Though I have to do some restructuring to make this available in VAB and flight ... hopefully I will not break too many eggs in the process.

untoldwind avatar Mar 05 '24 07:03 untoldwind

Thanks for your answer. If it's too complicated, don't worry about this. You certainly have a lot on your plate.

PhilouDS avatar Mar 05 '24 20:03 PhilouDS

There are some (potential) breaking changes: I noticed that ModuleCommand and ModuleDockingNode hat a lot of field from the underlying part, which does not translate well to the VAB version. So instead I added a .part field to all the modules (i.e. if your scripts are now missing a moduleDockingNode.something_important just use moduleDockingNode.part.something_important now).

Upside to this:

  • API is now more consistent
  • A lot of modules are now available in the VAB

Just a pre-release for now (too many changes ;) ) https://github.com/untoldwind/KontrolSystem2/releases/tag/v0.5.5.0 Documentation: https://kontrolsystem2.readthedocs.io/en/prerelease/

... and most importantly: I added bindings for ModuleLight. Your disco needs you!

untoldwind avatar Mar 05 '24 20:03 untoldwind

KSP2 tells me than Flight_plan is a now a dependency of this pre release version. Is it normal?

image

PhilouDS avatar Mar 05 '24 22:03 PhilouDS

@untoldwind I do not understand what you are talking about image

lefouvert avatar Mar 05 '24 23:03 lefouvert

@PhilouDS : Flightplan is marked as a soft dependency (i.e. should be optional) Some time ago we were testing out the possibility to include APIs to other mods. In theory:

  • If flightplan is installed if can be accessed via flight_plan() in ksp::addons
  • If it is not installed that function returns None.

Maybe the logic of soft dependency has changed in SpaceWarp recently, I will checkout the latest version

untoldwind avatar Mar 05 '24 23:03 untoldwind

@PhilouDS I just checked the newest pre-release 0.5.5.1 with the latest spacewarp 1.9.4 without flightplan installed, which works fine for me. Please check the BepInEx/LogOutput.log file in the game folder. It should contain some more detailed information why the plugin is suddenly creating problems

untoldwind avatar Mar 05 '24 23:03 untoldwind

I have installed the version 0.5.5.1 and I don't have the problem anymore. I don't know why but it's fine now.

PhilouDS avatar Mar 06 '24 10:03 PhilouDS

@PhilouDS I think I reproduced the problem by downgrading spacewarp to 1.6.0 was able to resolve the dependency issue by removing the BepInEx/cache folder

untoldwind avatar Mar 06 '24 20:03 untoldwind

Just to note : This deLICious .flat_map didn't have been reported on Ranges ^^ (I'm not sure their is any purpose to have a .filter_map on ranges since ranges are integer only)

lefouvert avatar Mar 07 '24 04:03 lefouvert

Pre-release 0.5.5.3 https://github.com/untoldwind/KontrolSystem2/releases/tag/v0.5.5.3 contains .flat_map and .filter_map for ranges. There actually might be some use-cases for the latter.

untoldwind avatar Mar 07 '24 21:03 untoldwind