Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LXNav S10 incorrect ballast synchronization #1319

Open
tatro77 opened this issue Dec 26, 2023 · 39 comments · May be fixed by #1326
Open

LXNav S10 incorrect ballast synchronization #1319

tatro77 opened this issue Dec 26, 2023 · 39 comments · May be fixed by #1326
Labels

Comments

@tatro77
Copy link

tatro77 commented Dec 26, 2023

XCSoar versions having and not having the problem

Current version 7.38

System information

Stefly Nav 7.0, Android 11.

Steps to reproduce the behavior

It is not possible to match the water ballast setting of the LXNav S10 and XCsoar.

Expected behavior

Ideally the settings for water ballast should match and drain at the same rate.

Actual behavior

Currently when 100L of water ballast are set in XCsoar (flight setup-custom), the LXNAV S10 shows 70L

Do you have any idea what may have caused this?

no

Do you have an idea how to solve the issue?

no
@tatro77 tatro77 changed the title LX Nav S10 incorrect ballast synchronization LXNav S10 incorrect ballast synchronization Dec 26, 2023
@tatro77
Copy link
Author

tatro77 commented Dec 26, 2023

IMG_9465

@lordfolken
Copy link
Contributor

Please switch on debug on the serial port and attach the resulting log.

@tatro77
Copy link
Author

tatro77 commented Dec 27, 2023 via email

@tatro77
Copy link
Author

tatro77 commented Dec 27, 2023

xcsoar.log

@lordfolken
Copy link
Contributor

The ballast is communicated as an overload factor.
$PLXV0, BAL,R/W,Overload factor*CS, where overload factor is defined as ratio between current mass and mass of glider, where polar was calculated

PLXV0,BA L,W,1.50

Please show us the configured plane profile.

@tatro77
Copy link
Author

tatro77 commented Dec 27, 2023

IMG_9496
IMG_9497
IMG_9498

@lordfolken
Copy link
Contributor

lordfolken commented Dec 27, 2023

So according to src/ActionInterface.ccp overload is defined as:

auto dry_mass = plane.empty_mass + polar.GetCrewMass();
auto overload = (dry_mass + ballast * plane.max_ballast) /
dry_mass;

However the LX Manual p.84 gives a different formula:
image
Reference weight corresponds to the weight value at which the polar was measured.

@hjr Can you check please?

@lordfolken lordfolken added the bug label Dec 27, 2023
@tatro77
Copy link
Author

tatro77 commented Dec 29, 2023

Thank you.
I believe the overload formula from the manual LX nav manual is correct. Overload =1.36 about 73kg when XCsoar shows 100l. I am not sure if I missed something.

@lordfolken
Copy link
Contributor

So this function is used centrally for several drivers. Therefore delicate testing is required.

In Summary:

Vaulter: Uses dry mass, would break with the change (would break)
Cai302: Uses Fraction (OK)
Borglet: Uses Cai302::PutBallast thefore fraction (OK)
LX1600: Uses reference Weight (currently broken)
LXNAV: Uses reference Weight (currently broken)
OpenVario: Uses overload and reference weight (currently broken)
XCVario: uses fraction, (OK)

src/Device/Driver/Vaulter.cpp:
Wing Load:
WL,
is a factor of the additional weight added by water ballast in relation to the reference weight of the glider
Example: $POV,C,WL,1.012 => No water ballast
$POV,C,WL,1.1
13 => 10% more weight than reference weight

So Vaulter would break changing this.

CAI302 uses fraction, therefore not a problem.

lx1600: According to current lxnavigation documentation:

char buffer[100];
sprintf(buffer, "PFLX2,,%.2f,,,,", overload);
PortWriteNMEA(port, buffer, env);

<load_factor> float Total glider mass divided by polar reference mass

Should be broken currently and the change would be appropriate

LXNAV:
SetBallast(Port &port, OperationEnvironment &env, double overload)
{
char buffer[100];
sprintf(buffer, "PLXV0,BAL,W,%.2f", overload);
PortWriteNMEA(port, buffer, env);
}

Overload Wrong

OpenVario:
bool
OpenVarioDevice::RepeatBallast(OperationEnvironment &env)
{
if (!_overload_valid) {
return InformUnavailable("WL", env);
}

char buffer[20];
sprintf(buffer,"POV,C,WL,%1.3f",(float)_overload);
PortWriteNMEA(port, buffer, env);
return true;
}

Wing Load:
WL,
is a factor of the additional weight added by water ballast in relation to the reference weight of the glider
Example: $POV,C,WL,1.012 => No water ballast
$POV,C,WL,1.1
13 => 10% more weight than reference weight

So also currently broken.

XCVario: uses fraction therefore not affected.

@MaxKellermann
Copy link
Contributor

However the LX Manual p.84 gives a different formula:

How is this formula different, and is this difference relevant for this bug?

(Please do not use the term "weight" - it's about mass, not weight. It's sad to see that even instrument manufacturers mix this up.)

The difference I see:

  • XCSoar uses "dry mass", which is "empty mass" plus "actual crew mass"
  • LXNAV uses "reference mass", which is "empty mass" plus "some unspecified crew mass"

So it's just how much crew is accounted for?

Right? Am I missing something?

@kobedegeest
Copy link
Contributor

How is this formula different, and is this difference relevant for this bug?

The difference lies in the denominator. XCSoar divides by dry mass which makes no sense as you want to get a scaling factor to rescale the glide polar which is calculated at the reference mass. So besides the incorrect use of the word weight the LX formula is the correct one. You can't get a scaling factor without the use of the mass used for the original calculation/measurement.

@MaxKellermann
Copy link
Contributor

@kobedegeest, so that's a "yes"?

XCSoar divides by dry mass which makes no sense

It makes no sense only with your personal definition of "scaling factor", but it does make sense with a different definition. When we discuss how to change XCSoar's formula, we need to consider the definition of each and every user of the term "scaling factor". XCSoar and LXNAV have a different definition, so this is really a bug, but as @lordfolken said we must change this formula only for LXNAV S10 and no other product (unless other products also have a different definition, which so far nobody has pointed out).

@lordfolken
Copy link
Contributor

lordfolken commented Jan 5, 2024

However the LX Manual p.84 gives a different formula:

How is this formula different, and is this difference relevant for this bug?

(Please do not use the term "weight" - it's about mass, not weight. It's sad to see that even instrument manufacturers mix this up.)

The difference I see:

* XCSoar uses "dry mass", which is "empty mass" plus "actual crew mass"

Only when it is talking to the drivers when updating the PutBallast();

It also uses the polar reference_mass for the glide polar calculation:

/** Reference mass of reference_polar, kg */
double reference_mass;

* LXNAV uses "reference mass", which is "empty mass" plus "some unspecified crew mass"

The reference mass is the mass at which the polar was measured.

So the first number from polarstore.hpp

{ _T("LS-8 (15m)"), 325, 185, 70, -0.51, 115, -0.85, 173, -2.00, 10.5, 0.0, 108, 240 },

325kg for the ls8(15m).

The overload factor is defined as the current total mass (crew + empty plane + water ballast) / (the polar reference mass)

So basically if you had a 255 empty plane + a 70kg piloy = 325. So your overload factor towards the polar would be 1.

@MaxKellermann
Copy link
Contributor

The overload factor is defined as the current total mass (crew + empty plane + water ballast) / (the polar reference mass)

Who exactly defines it that way, and which devices agree with this definition?

Take this document for example: https://www.lx-avionik.de/wp/download/manuals/LXS3ManualEnglishVer0101.pdf page 20
Instead of "reference mass", it uses the "minimum glider weight", which is defined as "Minimum glider weight is a weight of empty glider + minimum pilot weight". This is different from "reference mass" = "mass at which the polar was measured".

So now we already have two documents by one manufacturer (LXNAV) with different definitions of "overload factor". Sh*t!

@lordfolken
Copy link
Contributor

@kobedegeest, so that's a "yes"?

XCSoar divides by dry mass which makes no sense

It makes no sense only with your personal definition of "scaling factor", but it does make sense with a different definition. When we discuss how to change XCSoar's formula, we need to consider the definition of each and every user of the term "scaling factor". XCSoar and LXNAV have a different definition, so this is really a bug, but as @lordfolken said we must change this formula only for LXNAV S10 and no other product (unless other products also have a different definition, which so far nobody has pointed out).

I went through all the documentation i could find when i researched #1319 (comment)

The overload factor is always divided by the reference_mass, for all mentioned products, except for the vaulter driver.
So currently broken are the following drivers:

  • lxnav
  • lxnavigation
  • OpenVario

According to the vaulter manual, its the only one to use dry mass.
image

@MaxKellermann
Copy link
Contributor

The LXNAV LX9000 manual also uses "minimum glider weight", not "reference mass".

@lordfolken
Copy link
Contributor

@MaxKellermann
Copy link
Contributor

I don't think XCSoar should put a meaning in "overload factor". It's a bad metric, it's useless for calculations (you need to derive the ballast mass from it before you can do anything, so why not just use the ballast mass in the first place?), and there are (at least) three valid contradicting definitions for it.
Each device driver which needs an "overload factor" should calculate it whenever needed using the definition from that device's manufacturer.
We shouldn't care how many definitions there are, as long as we use the right one for each device.

@lordfolken
Copy link
Contributor

So according to the protocol specification 1.04 from LXNAV:

image

image

@lordfolken
Copy link
Contributor

I´ll contact them and ask them whether this is a bug in the docs...

@lordfolken
Copy link
Contributor

I don't think XCSoar should put a meaning in "overload factor". It's a bad metric, it's useless for calculations (you need to derive the ballast mass from it before you can do anything, so why not just use the ballast mass in the first place?)

Every's plane dry mass is different. For the polar it makes sense to take the weight at which a,b,c, is defined, and then transform it to the current total mass. How much of that is ballast / crew or else isn't really relevant.

@MaxKellermann
Copy link
Contributor

Of course, for glide path calculations, only the actual mass (or rather: weight! yes! .. which includes G load) of the glider is relevant, and knowing the amount of pilot mass vs water ballast doesn't matter here. But this misses the point I made, because the current mass of the glider is not a primary metric, but a calculated value. I was talking about primary metrics, and the ballast mass is a good primary metric (one that the user enters because they know how much water they filled into the wings); the overload factor is a very bad primary metric and also a very bad calculated metric. The overload factor should not be something that is entered by anybody or transferred to/from a device. It could be displayed if the user is curious about this value, but it's really not useful for anything.
In my opinion, a device that allows transferring the overload factor over its machine-readable protocol is not well-designed.

@lordfolken
Copy link
Contributor

I have access to an S80, which runs the same firmware as the S100.

Some testing just on the vario side and observing the NMEA traffic.

             
Reference Weight Pilot Weight Empty Weight   Ballast xcsoar bal LXWP2
300 0 200   40   0.8
300 40 200   0   0.8
300 0 100   140   0.8
300 100 200   0   1
300 120 200   0   1.06
300 130 200   0   1.1

Some observations

  • the minimum value that is available for overload is 0.8
  • the overload is clearly divided by the reference mass
  • the vario doesn't allow a pilot below 40 kg or ballast of 40 kg
  • setting reference weight = pilot + empty weight will fix the synchronization between xcsoar and s80/s100

@lordfolken
Copy link
Contributor

lordfolken commented Jan 7, 2024

Rereading the S7 Manual:
image

So even if they call it minimum weight, they actually mean refrence weight.

@kobedegeest
Copy link
Contributor

I went through all the documentation i could find when i researched #1319 (comment)

The overload factor is always divided by the reference_mass, for all mentioned products, except for the vaulter driver. So currently broken are the following drivers:

  • lxnav
  • lxnavigation
  • OpenVario

According to the vaulter manual, its the only one to use dry mass. image

I don't think vaulter would break if the formula gets changed in XCSoar. On page 27 they give an example of how the polar in vaulter works (hardly a polar rather best LD):
image

So how I understand if you set the V_LD and LD of your glider at reference mass of your polar used by XCSoar. Then the overload factor defined by LXnav would be the correct value to transmit. The manual also does not say that that value need to be more then 100% (but of course that is no guarantee). Current users with working setup would have to go and change their settings but they would have to do this every time the pilot mass changes anyway with current implementation.

@lordfolken
Copy link
Contributor

So yes, all the weights have to be entered manually on both sides.

The Vaulter vario uses the following command to set the overload factor:

$PITV1,WL=1.35

According to the Protocol docs: "Set the wing loading factor (ratio of no-ballast to weight)"

@lordfolken
Copy link
Contributor

Oh, the fix for vaulter is easy. I use the fraction and add +1.

@lordfolken lordfolken linked a pull request Jan 8, 2024 that will close this issue
@kobedegeest
Copy link
Contributor

Oh, the fix for vaulter is easy. I use the fraction and add +1.

Isn't fraction defined as ballast/max_ballast? At least according to the documentation of the CAI302 that is what fraction is defined as. How can fraction +1 be correct for vaulter? Unless for the rare case that you can exactly double your mass with water this will not give the correct value. Vaulter has no knowledge of max_ballast. It only knows best_LD and V_LD at some mass which it takes as 100%. If you are 35% heavier than this mass you send:
$PITV1,WL=1.35

What the user decides to be this 100% seems (soly based on the manual) not to strictly matter and then reference mass of the polar seems logical (since dry mass is not a constant as it changes based on the pilot). And if this weight/ wingloading factor cannot be smaller than 100% it is similar to S3 and S7 varios and you have to use min take off mass.

@lordfolken
Copy link
Contributor

So basically, using the overload as newly defined is correct, and no special provision is needed for vaulter.

@kobedegeest
Copy link
Contributor

Out of curiosity and on the topic of syncing ballast. Is the polar info automatically synced between XCSoar and LX nav (like is the case for CAI302 for example) or does the user need to set this by himself manually? As currently XCSoar and LX nav input the polar data in a different way checking if both match is not as simple as checking if the numbers match. LX nav uses a,b,c coefs of the quadratic approximation and XCSoar uses 3 data points on the polar. (LXNav uses -W = a/10 000 *V^2 + b/100 *V + c, as a formula.)

It is also not documented anywhere, at the moment, that both devices need to be using a polar calculated at the same reference mass for ballast to be synced, which is a non trivial requirement imposed by how LXnav receives this data.

@MaxKellermann MaxKellermann linked a pull request Feb 26, 2024 that will close this issue
@lordfolken
Copy link
Contributor

I'm finding more and more issues here...

void SetBallastLitres(const double litres) noexcept;
/**
* Retrieve ballast
* @return Proportion of possible ballast [0-1]
*/
constexpr double GetBallast() const noexcept {
return ballast / (ballast_ratio * reference_mass);
}

Why would you use the polar reference mass to determine a ballast fraction?
This should be IMO empty weight + crew.

@kobedegeest
Copy link
Contributor

ballast_ratio is defined set as plane.max_ballast / plane.polar_shape.reference_mass
https://github.com/XCSoar/XCSoar/blob/master/src/Plane/PlaneGlue.cpp#L74

So the formula you show is mostlikly correct. But why even have or use a ballast_ratio is a better question everywhere that it is used it is multiplied by reference_mass. As far as i see. So it would make more sense to get ride of the ballast_ratio variable and use plane.max_ballast instead.

@lordfolken
Copy link
Contributor

lordfolken commented Apr 23, 2024

So what is this then?

/** Ratio of mass of ballast to glider empty weight */
double ballast_ratio;

The reference mass is the mass at which the glide polar was measured at.
This includes the sum of an unknown empty weight of a glider with and unknown weight of a pilot. Its not the empty weight of the current glider, nor the empty weight of the glider the polar was measured at.

@kobedegeest
Copy link
Contributor

d726d38 is where that got introduced first (which was in 2009) so not sure if you should trust that in code documentation.

It got used like this

GlidePolar::get_ballast_litres() const 
{
  return ballast*ballast_ratio*empty_mass;
}

which you can see after it got merched in 38193e0

So back then it did use empty mass but not anymore i think. (The code documentation should also say "ratio of max ballast to glider empty weight" to be correct)

@lordfolken
Copy link
Contributor

So back then it did use empty mass but not anymore i think. (The code documentation should also say "ratio of max ballast to glider empty weight" to be correct)

What do you do with this info?

  • you cannot use it for the ballast timer, as you cannot also drop the pilot
  • using reference mass is also wrong, as that is a mass that only exists for the polar and isn't based on current reality.
  • you cannot even use this info for the vaulter driver, as its refereeing to the "gross weight" being 100% and the water ballast the added percentage. (so 100% = empty plane + pilot)

Also stuff like this scares me:

empty_mass(reference_mass),

All of this just happens to work, because in the unit test, reference mass was set to the same as drymass:

polar.SetReferenceMass(318, false);
polar.SetEmptyMass(228, false);
polar.SetCrewMass(90, false);
polar.SetBallastRatio(100 / polar.reference_mass);

@kobedegeest i updated my pr in overload2, to address some of this. Of course the unit tests fail at the moment.

@lordfolken
Copy link
Contributor

This is the "default polar".

_T("LS-8 (15m)"), 325, 185, 70, -0.51, 115, -0.85, 173, -2.00, 10.5, 0.0, 108, 240,

Imo .51 min sink? Did someone use the 18m polar, and rename it to 15m? Not even the manufacturer is that optimistic.

@lordfolken
Copy link
Contributor

lordfolken commented Apr 27, 2024

Can't be right:

{ _T("LS-8 (15m)"), 325, 185, 70, -0.51, 115, -0.85, 173, -2.00, 10.5, 0.0, 108, 240 },
{ _T("LS-8 (18m)"), 325, 185, 80, -0.51, 94, -0.56, 173, -2.00, 11.4, 0.0, 114, 250 },

image

@kobedegeest
Copy link
Contributor

What do you do with this info?

  • you cannot use it for the ballast timer, as you cannot also drop the pilot
  • using reference mass is also wrong, as that is a mass that only exists for the polar and isn't based on current reality.
  • you cannot even use this info for the vaulter driver, as its refereeing to the "gross weight" being 100% and the water ballast the added percentage. (so 100% = empty plane + pilot)

Indeed which is why it was multiplied with empty mass everywhere. So I agree balast_ratio should be removed.

All of this just happens to work, because in the unit test, reference mass was set to the same as drymass:

So just to fill in my gap in knowledge how does unit test work? How does it know the formulas are wrong/correct?

@lordfolken
Copy link
Contributor

So just to fill in my gap in knowledge how does unit test work? How does it know the formulas are wrong/correct?

In short: You set the inputs to certain values, let the code run, and compare the outputs to what you (manually predetermined) what they should be.
Should the results not match, or any part of the code influence that, you know that your change broke something.
Theory says, that every-time you have bug, you should write a unit-test running on the pipeline to prevent that bug from happening again.
A better more detailed explanation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants