1 /* Copyright 2002-2024 CS GROUP 2 * Licensed to CS GROUP (CS) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * CS licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.orekit.utils; 18 19 import java.util.ArrayList; 20 import java.util.Collections; 21 import java.util.Iterator; 22 import java.util.List; 23 import java.util.Map; 24 25 import org.hipparchus.analysis.differentiation.Gradient; 26 import org.hipparchus.util.FastMath; 27 import org.hipparchus.util.Precision; 28 import org.orekit.errors.OrekitException; 29 import org.orekit.errors.OrekitIllegalStateException; 30 import org.orekit.errors.OrekitMessages; 31 import org.orekit.propagation.events.ParameterDrivenDateIntervalDetector; 32 import org.orekit.time.AbsoluteDate; 33 import org.orekit.utils.TimeSpanMap.Span; 34 import org.orekit.utils.TimeSpanMap.Transition; 35 36 37 /** Class allowing to drive the value of a parameter. 38 * <p> 39 * This class is typically used as a bridge between an estimation 40 * algorithm (typically orbit determination or optimizer) and an 41 * internal parameter in a physical model that needs to be tuned, 42 * or a bridge between a finite differences algorithm and an 43 * internal parameter in a physical model that needs to be slightly 44 * offset. The physical model will expose to the algorithm a 45 * set of instances of this class so the algorithm can call the 46 * {@link #setValue(double, AbsoluteDate)} method to update the 47 * parameter value at a given date. Some parameters driver only have 1 value estimated/driven 48 * over the all period (constructor by default). Some others have several 49 * values estimated/driven on several periods/intervals. For example if the time period is 3 days 50 * for a drag parameter estimated all days then 3 values would be estimated, one for 51 * each time period. In order to allow several values to be estimated, the PDriver has 52 * a name and a value {@link TimeSpanMap} as attribute. In order, 53 * to cut the time span map there are 2 options : 54 * </p> 55 * <ul> 56 * <li>Passive cut calling the {@link #addSpans(AbsoluteDate, AbsoluteDate, double)} method. 57 * Given a start date, an end date and and a validity period (in sec) 58 * for the driver, the {@link #addSpans} method will cut the interval of name and value time span map 59 * from start date to date end in several interval of validity period duration. This method should not 60 * be called on orbital drivers and must be called only once at beginning of the process (for example 61 * beginning of orbit determination). <b>WARNING : In order to ensure converge for orbit determination, 62 * the start, end date and driver periodicity must be wisely choosen </b>. There must be enough measurements 63 * on each interval or convergence won't reach or singular matrixes will appear. </li> 64 * <li> Active cut calling the {@link #addSpanAtDate(AbsoluteDate)} method. 65 * Given a date, the method will cut the value and name time span name, in order to have a new span starting at 66 * the given date. Can be called several time to cut the time map as wished. <b>WARNING : In order to ensure 67 * converge for orbit determination, if the method is called several time, the start date must be wisely choosen </b>. 68 * There must be enough measurements on each interval or convergence won't reach or singular matrixes will appear. </li> 69 * </ul> 70 * <p> 71 * Several ways exist in order to get a ParameterDriver value at a certain 72 * date for parameters having several values on several intervals. 73 * </p> 74 * <ul> 75 * <li>First of all, the step estimation, that is to say, if a value wants 76 * to be known at a certain date, the value returned is the one of span 77 * beginning corresponding to the date. With this definition a value 78 * will be kept all along the span duration and will be the value of the span 79 * start.</li> 80 * <li> The continuous estimation, that is to say, when a value wants be to 81 * known at a date t, the value returned would be a linear interpolation between 82 * the value at the beginning of the span corresponding to date t and end this span 83 * (which is also the beginning of next span). NOT IMPLEMENTED FOR NOW 84 * </li> 85 * </ul> 86 * Each time the value is set, the physical model 87 * will be notified as it will register a {@link ParameterObserver 88 * ParameterObserver} for this purpose. 89 * <p> 90 * This design has two major goals. First, it allows an external 91 * algorithm to drive internal parameters almost anonymously, as it only 92 * needs to get a list of instances of this class, without knowing 93 * what they really drive. Second, it allows the physical model to 94 * not expose directly setters methods for its parameters. In order 95 * to be able to modify the parameter value, the algorithm 96 * <em>must</em> retrieve a parameter driver. 97 * </p> 98 * @see ParameterObserver 99 * @author Luc Maisonobe 100 * @author Melina Vanel 101 * @since 8.0 102 */ 103 public class ParameterDriver { 104 105 /** Name of the parameter.*/ 106 public static final String SPAN = "Span"; 107 108 /** Name of the parameter. */ 109 private String name; 110 111 /** TimeSpan for period names. 112 * @since 12.0 113 */ 114 private TimeSpanMap<String> nameSpanMap; 115 116 /** Reference value. */ 117 private double referenceValue; 118 119 /** Scaling factor. */ 120 private double scale; 121 122 /** Minimum value. */ 123 private double minValue; 124 125 /** Maximum value. */ 126 private double maxValue; 127 128 /** Reference date. 129 * @since 9.0 130 */ 131 private AbsoluteDate referenceDate; 132 133 /** Flag to choose estimation method. If estimationContinuous 134 * is true then when a value wants to be known an interpolation 135 * is performed between given date span start and end (start of 136 * next span) otherwise the value returned is the value of span start 137 * @since 12.0 138 */ 139 private boolean isEstimationContinuous; 140 141 /** Value time span map. 142 * @since 12.0 143 */ 144 private TimeSpanMap<Double> valueSpanMap; 145 146 /** Selection status. 147 * <p> 148 * Selection is used for estimated parameters in orbit determination, 149 * or to compute the Jacobian matrix in partial derivatives computation. 150 * </p> 151 */ 152 private boolean selected; 153 154 /** Observers observing this driver. */ 155 private final List<ParameterObserver> observers; 156 157 /** Create a new instance from another parameterDriver informations 158 * for example (useful for {@link ParameterDriversList.DelegatingDriver})) 159 * At construction, the parameter new is configured as <em>not</em> selected, 160 * the reference date is set to {@code null}. validityPeriod, namesSpanMap and 161 * valueSpanMap. 162 * @param name general name of the parameter 163 * @param namesSpanMap name time span map. WARNING, number of Span must be coherent with 164 * validityPeriod and valueSpanMap (same number of Span with same transitions 165 * dates) 166 * @param valuesSpanMap values time span map 167 * @param referenceValue reference value of the parameter 168 * @param scale scaling factor to convert the parameters value to 169 * non-dimensional (typically set to the expected standard deviation of the 170 * parameter), it must be non-zero 171 * @param minValue minimum value allowed 172 * @param maxValue maximum value allowed 173 * @since 12.0 174 */ 175 public ParameterDriver(final String name, final TimeSpanMap<String> namesSpanMap, 176 final TimeSpanMap<Double> valuesSpanMap, final double referenceValue, 177 final double scale, final double minValue, final double maxValue) { 178 if (FastMath.abs(scale) <= Precision.SAFE_MIN) { 179 throw new OrekitException(OrekitMessages.TOO_SMALL_SCALE_FOR_PARAMETER, 180 name, scale); 181 } 182 this.name = name; 183 this.nameSpanMap = namesSpanMap; 184 this.referenceValue = referenceValue; 185 this.scale = scale; 186 this.minValue = minValue; 187 this.maxValue = maxValue; 188 this.referenceDate = null; 189 this.valueSpanMap = valuesSpanMap; 190 this.selected = false; 191 this.observers = new ArrayList<>(); 192 this.isEstimationContinuous = false; 193 } 194 195 /** Simple constructor. 196 * <p> 197 * At construction, the parameter is configured as <em>not</em> selected, 198 * the reference date is set to {@code null}, the value is set to the 199 * {@code referenceValue}, the validity period is set to 0 so by default 200 * the parameterDriver will be estimated on only 1 interval from -INF to 201 * +INF. To change the validity period the 202 * {@link ParameterDriver#addSpans(AbsoluteDate, AbsoluteDate, double)} 203 * method must be called. 204 * </p> 205 * @param name name of the parameter 206 * @param referenceValue reference value of the parameter 207 * @param scale scaling factor to convert the parameters value to 208 * non-dimensional (typically set to the expected standard deviation of the 209 * parameter), it must be non-zero 210 * @param minValue minimum value allowed 211 * @param maxValue maximum value allowed 212 */ 213 public ParameterDriver(final String name, 214 final double referenceValue, final double scale, 215 final double minValue, final double maxValue) { 216 if (FastMath.abs(scale) <= Precision.SAFE_MIN) { 217 throw new OrekitException(OrekitMessages.TOO_SMALL_SCALE_FOR_PARAMETER, 218 name, scale); 219 } 220 this.name = name; 221 this.nameSpanMap = new TimeSpanMap<>(SPAN + name + Integer.toString(0)); 222 this.referenceValue = referenceValue; 223 this.scale = scale; 224 this.minValue = minValue; 225 this.maxValue = maxValue; 226 this.referenceDate = null; 227 // at construction the parameter driver 228 // will be consider with only 1 estimated value over the all orbit 229 // determination 230 this.valueSpanMap = new TimeSpanMap<>(referenceValue); 231 this.selected = false; 232 this.observers = new ArrayList<>(); 233 this.isEstimationContinuous = false; 234 } 235 236 /** Get current name span map of the parameterDriver, cut in interval 237 * in accordance with value span map and validity period. 238 * @return current name span map 239 * @since 12.0 240 */ 241 public TimeSpanMap<String> getNamesSpanMap() { 242 return nameSpanMap; 243 } 244 245 /** Get value time span map for parameterDriver. 246 * @return value time span map 247 * @since 12.0 248 */ 249 public TimeSpanMap<Double> getValueSpanMap() { 250 return valueSpanMap; 251 } 252 253 /** Set current parameter value span map to match another driver. In order to keep 254 * consistency, the validity period and name span map are updated. 255 * @param driver for which the value span map wants to be copied for the 256 * current driver 257 * @since 12.0 258 */ 259 public void setValueSpanMap(final ParameterDriver driver) { 260 final TimeSpanMap<Double> previousValueSpanMap = driver.getValueSpanMap(); 261 valueSpanMap = driver.getValueSpanMap(); 262 nameSpanMap = driver.getNamesSpanMap(); 263 for (final ParameterObserver observer : observers) { 264 observer.valueSpanMapChanged(previousValueSpanMap, this); 265 } 266 } 267 268 /** Get the number of values to estimate that is to say the number. 269 * of Span present in valueSpanMap 270 * @return int the number of values to estimate 271 * @since 12.0 272 */ 273 public int getNbOfValues() { 274 return valueSpanMap.getSpansNumber(); 275 } 276 277 /** Get the dates of the transitions for the drag sensitive models {@link TimeSpanMap}. 278 * @return dates of the transitions for the drag sensitive models {@link TimeSpanMap} 279 * @since 12.0 280 */ 281 public AbsoluteDate[] getTransitionDates() { 282 283 // Get all transitions 284 final List<AbsoluteDate> listDates = new ArrayList<>(); 285 286 // Extract all the transitions' dates 287 for (Transition<Double> transition = getValueSpanMap().getFirstSpan().getEndTransition(); transition != null; transition = transition.next()) { 288 listDates.add(transition.getDate()); 289 } 290 // Return the array of transition dates 291 return listDates.toArray(new AbsoluteDate[0]); 292 } 293 294 /** Get all values of the valueSpanMap in the chronological order. 295 * @return double[] containing values of the valueSpanMap in the chronological order 296 */ 297 public double[] getValues() { 298 final double[] chronologicalValues = new double[getNbOfValues()]; 299 Span<Double> currentSpan = valueSpanMap.getFirstSpan(); 300 for (int i = 0; i < getNbOfValues() - 1; i++) { 301 chronologicalValues[i] = currentSpan.getData(); 302 currentSpan = currentSpan.next(); 303 } 304 chronologicalValues[getNbOfValues() - 1 ] = currentSpan.getData(); 305 return chronologicalValues; 306 } 307 308 309 /** Add an observer for this driver. 310 * <p> 311 * The observer {@link ParameterObserver#valueSpanMapChanged(TimeSpanMap, ParameterDriver) 312 * valueSpanMapChanged} method is called once automatically when the 313 * observer is added, and then called at each value change. 314 * </p> 315 * @param observer observer to add 316 * while being updated 317 */ 318 public void addObserver(final ParameterObserver observer) { 319 observers.add(observer); 320 observer.valueSpanMapChanged(getValueSpanMap(), this); 321 } 322 323 /** Remove an observer. 324 * @param observer observer to remove 325 * @since 9.1 326 */ 327 public void removeObserver(final ParameterObserver observer) { 328 for (final Iterator<ParameterObserver> iterator = observers.iterator(); iterator.hasNext();) { 329 if (iterator.next() == observer) { 330 iterator.remove(); 331 return; 332 } 333 } 334 } 335 336 /** Replace an observer. 337 * @param oldObserver observer to replace 338 * @param newObserver new observer to use 339 * @since 10.1 340 */ 341 public void replaceObserver(final ParameterObserver oldObserver, final ParameterObserver newObserver) { 342 for (int i = 0; i < observers.size(); ++i) { 343 if (observers.get(i) == oldObserver) { 344 observers.set(i, newObserver); 345 } 346 } 347 } 348 349 /** Get the observers for this driver. 350 * @return an unmodifiable view of the observers for this driver 351 * @since 9.1 352 */ 353 public List<ParameterObserver> getObservers() { 354 return Collections.unmodifiableList(observers); 355 } 356 357 /** Get parameter driver general name. 358 * @return name 359 */ 360 public String getName() { 361 return name; 362 } 363 364 /** Get name of the parameter span for a specific date. 365 * @param date date at which the name of the span wants to be known 366 * @return name data of the name time span map at date 367 */ 368 public String getNameSpan(final AbsoluteDate date) { 369 return nameSpanMap.get(date); 370 } 371 372 /** Change the general name of this parameter driver. 373 * @param name new name 374 */ 375 public void setName(final String name) { 376 final String previousName = this.name; 377 this.name = name; 378 for (final ParameterObserver observer : observers) { 379 observer.nameChanged(previousName, this); 380 } 381 // the names time span map must also be updated with the new name 382 if (nameSpanMap.getSpansNumber() > 1) { 383 Span<String> currentNameSpan = nameSpanMap.getFirstSpan(); 384 nameSpanMap.addValidBefore(SPAN + name + Integer.toString(0), currentNameSpan.getEnd(), false); 385 for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); ++spanNumber) { 386 currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd()); 387 nameSpanMap.addValidAfter(SPAN + name + Integer.toString(spanNumber), currentNameSpan.getStart(), false); 388 } 389 } else { 390 nameSpanMap = new TimeSpanMap<>(SPAN + name + Integer.toString(0)); 391 } 392 } 393 394 /** Cut values and names time span map given orbit determination start and end and driver 395 * periodicity. 396 * <p> 397 * For example for a drag coefficient the validity period would be 398 * 1 days = 86400sec. To be called after constructor to cut the temporal axis with 399 * the wanted parameter driver temporality for estimations on the wanted interval. 400 * </p> 401 * <p> 402 * Must be called only once at the beginning of orbit 403 * determination for example. If called several times, will throw exception. If parameter 404 * estimations intervals must be changed then a new ParameterDriver must be created or the 405 * function {@link #addSpanAtDate} should be used. 406 * </p> 407 * <p> 408 * This function should not be called on {@link DateDriver} and 409 * any of {@link ParameterDrivenDateIntervalDetector} attribute, because there is no sense to 410 * estimate several values for dateDriver. 411 * </p> 412 * <p> 413 * The choice of {@code orbitDeterminationStartDate}, {@code orbitDeterminationEndDate} and 414 * {@code validityPeriodForDriver} in a case of orbit determination must be done carefully, 415 * indeed, enough measurement should be available for each time interval or 416 * the orbit determination won't converge. 417 * </p> 418 * @param orbitDeterminationStartDate start date for which the parameter driver 419 * starts to be estimated. 420 * @param orbitDeterminationEndDate end date for which the parameter driver 421 * stops to be estimated. 422 * @param validityPeriodForDriver validity period for which the parameter value 423 * is effective (for example 1 day for drag coefficient). Warning, validityPeriod 424 * should not be too short or the orbit determination won't converge. 425 * @since 12.0 426 */ 427 public void addSpans(final AbsoluteDate orbitDeterminationStartDate, 428 final AbsoluteDate orbitDeterminationEndDate, 429 final double validityPeriodForDriver) { 430 431 // by convention 0 is when the parameter needs to be drived only on 1 432 // interval from -INF to +INF time period 433 if (getNbOfValues() != 1) { 434 // throw exception if called several time, must be called only once at the beginning of orbit 435 // determination, if the periods wants to be changed a new parameter must be created 436 throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_PERIODS_HAS_ALREADY_BEEN_SET, name); 437 } else { 438 439 int spanNumber = 1; 440 AbsoluteDate currentDate = orbitDeterminationStartDate.shiftedBy(validityPeriodForDriver); 441 //splitting the names and values span map accordingly with start and end of orbit determination 442 //and validity period. A security is added to avoid having to few measurements point for a span 443 //in order to assure orbit determination convergence 444 while (currentDate.isBefore(orbitDeterminationEndDate) && orbitDeterminationEndDate.durationFrom(currentDate) > validityPeriodForDriver / 3.0) { 445 valueSpanMap.addValidAfter(getValue(currentDate), currentDate, false); 446 nameSpanMap.addValidAfter(SPAN + getName() + Integer.toString(spanNumber++), currentDate, false); 447 currentDate = currentDate.shiftedBy(validityPeriodForDriver); 448 } 449 } 450 } 451 452 /** Create a new span in values and names time span map given a start date. 453 * <b> One must be aware of the importance of choosing wise dates if this function is called 454 * several times to create several span at wanted times. Indeed, if orbit determination is performed 455 * it might not converge or find singular matrix if the spans are too short and contains to few measurements. 456 * Must be called before any computation (for example before 457 * orbit determination).</b> 458 * @param spanStartDate wanted start date for parameter value interval 459 * starts to be estimated. 460 * @since 12.0 461 */ 462 public void addSpanAtDate(final AbsoluteDate spanStartDate) { 463 464 // Split value span map with new interval having for start date spanStartDate and end 465 // date next span start date of +INF if no span is present after 466 valueSpanMap.addValidAfter(getValue(spanStartDate), spanStartDate, false); 467 nameSpanMap.addValidAfter(name, spanStartDate, false); 468 // Rename spans recursively 469 Span<String> currentNameSpan = nameSpanMap.getFirstSpan(); 470 nameSpanMap.addValidBefore(SPAN + name + Integer.toString(0), currentNameSpan.getEnd(), false); 471 472 for (int spanNumber = 1; spanNumber < nameSpanMap.getSpansNumber(); spanNumber++) { 473 currentNameSpan = nameSpanMap.getSpan(currentNameSpan.getEnd()); 474 nameSpanMap.addValidAfter(SPAN + name + Integer.toString(spanNumber), currentNameSpan.getStart(), false); 475 } 476 } 477 478 /** Get reference parameter value. 479 * @return reference parameter value 480 */ 481 public double getReferenceValue() { 482 return referenceValue; 483 } 484 485 /** Set reference parameter value. 486 * @since 9.3 487 * @param referenceValue the reference value to set. 488 */ 489 public void setReferenceValue(final double referenceValue) { 490 final double previousReferenceValue = this.referenceValue; 491 this.referenceValue = referenceValue; 492 for (final ParameterObserver observer : observers) { 493 observer.referenceValueChanged(previousReferenceValue, this); 494 } 495 } 496 497 /** Get minimum parameter value. 498 * @return minimum parameter value 499 */ 500 public double getMinValue() { 501 return minValue; 502 } 503 504 /** Set minimum parameter value. 505 * @since 9.3 506 * @param minValue the minimum value to set. 507 */ 508 public void setMinValue(final double minValue) { 509 final double previousMinValue = this.minValue; 510 this.minValue = minValue; 511 for (final ParameterObserver observer : observers) { 512 observer.minValueChanged(previousMinValue, this); 513 } 514 // Check if all values are still not out of min/max range 515 for (Span<Double> span = valueSpanMap.getFirstSpan(); span != null; span = span.next()) { 516 setValue(getValue(span.getStart()), span.getStart()); 517 } 518 } 519 520 /** Get maximum parameter value. 521 * @return maximum parameter value 522 */ 523 public double getMaxValue() { 524 return maxValue; 525 } 526 527 /** Set maximum parameter value. 528 * @since 9.3 529 * @param maxValue the maximum value to set. 530 */ 531 public void setMaxValue(final double maxValue) { 532 final double previousMaxValue = this.maxValue; 533 this.maxValue = maxValue; 534 for (final ParameterObserver observer : observers) { 535 observer.maxValueChanged(previousMaxValue, this); 536 } 537 // Check if all values are still not out of min/max range 538 for (Span<Double> span = valueSpanMap.getFirstSpan(); span != null; span = span.next()) { 539 setValue(getValue(span.getStart()), span.getStart()); 540 } 541 } 542 543 /** Get scale. 544 * @return scale 545 */ 546 public double getScale() { 547 return scale; 548 } 549 550 /** Set scale. 551 * @since 9.3 552 * @param scale the scale to set. 553 */ 554 public void setScale(final double scale) { 555 final double previousScale = this.scale; 556 this.scale = scale; 557 for (final ParameterObserver observer : observers) { 558 observer.scaleChanged(previousScale, this); 559 } 560 } 561 562 /** Get normalized value at specific date. 563 * <p> 564 * The normalized value is a non-dimensional value 565 * suitable for use as part of a vector in an optimization 566 * process. It is computed as {@code (current - reference)/scale}. 567 * </p> 568 * @param date date for which the normalized value wants to be known 569 * @return normalized value 570 */ 571 public double getNormalizedValue(final AbsoluteDate date) { 572 return (getValue(date) - getReferenceValue()) / scale; 573 } 574 575 /** Get normalized value. Only useable on ParameterDriver 576 * which have only 1 span on their TimeSpanMap value (that is 577 * to say for which the setPeriod method wasn't called) otherwise 578 * it will throw an exception. 579 * <p> 580 * The normalized value is a non-dimensional value 581 * suitable for use as part of a vector in an optimization 582 * process. It is computed as {@code (current - reference)/scale}. 583 * </p> 584 * @return normalized value 585 */ 586 public double getNormalizedValue() { 587 return (getValue() - getReferenceValue()) / scale; 588 } 589 590 /** Set normalized value at specific date. 591 * <p> 592 * The normalized value is a non-dimensional value 593 * suitable for use as part of a vector in an optimization 594 * process. It is computed as {@code (current - reference)/scale}. 595 * </p> 596 * @param date date for which the normalized value wants to be set 597 * @param normalized value 598 */ 599 public void setNormalizedValue(final double normalized, final AbsoluteDate date) { 600 setValue(getReferenceValue() + scale * normalized, date); 601 } 602 603 /** Set normalized value at specific date. Only useable on ParameterDriver 604 * which have only 1 span on their TimeSpanMap value (that is 605 * to say for which the setPeriod method wasn't called) otherwise 606 * it will throw an exception. 607 * <p> 608 * The normalized value is a non-dimensional value 609 * suitable for use as part of a vector in an optimization 610 * process. It is computed as {@code (current - reference)/scale}. 611 * </p> 612 * @param normalized value 613 */ 614 public void setNormalizedValue(final double normalized) { 615 setValue(getReferenceValue() + scale * normalized); 616 } 617 618 /** Get current reference date. 619 * @return current reference date (null if it was never set) 620 * @since 9.0 621 */ 622 public AbsoluteDate getReferenceDate() { 623 return referenceDate; 624 } 625 626 /** Set reference date. 627 * @param newReferenceDate new reference date 628 * @since 9.0 629 */ 630 public void setReferenceDate(final AbsoluteDate newReferenceDate) { 631 final AbsoluteDate previousReferenceDate = getReferenceDate(); 632 referenceDate = newReferenceDate; 633 for (final ParameterObserver observer : observers) { 634 observer.referenceDateChanged(previousReferenceDate, this); 635 } 636 } 637 638 /** Get current parameter value. Only usable on ParameterDriver 639 * which have only 1 span on their TimeSpanMap value (that is 640 * to say for which the setPeriod method wasn't called) 641 * @return current parameter value 642 */ 643 public double getValue() { 644 if (getNbOfValues() > 1) { 645 throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, name, "getValue(date)"); 646 } 647 // Attention voir si qlqchose est retourné si une exception est levée 648 return valueSpanMap.getFirstSpan().getData(); 649 } 650 651 /** Get current parameter value at specific date, depending on isContinuousEstimation 652 * value, the value returned will be obtained by step estimation or continuous estimation. 653 * @param date date for which the value wants to be known. Only if 654 * parameter driver has 1 value estimated over the all orbit determination 655 * period (not validity period intervals for estimation), the date value can 656 * be <em>{@code null}</em> and then the only estimated value will be 657 * returned, in this case the date can also be whatever the value returned would 658 * be the same. Moreover in this particular case one can also call the {@link #getValue()}. 659 * @return current parameter value at date date, or for the all period if 660 * no validity period (= 1 value estimated over the all orbit determination 661 * period) 662 */ 663 public double getValue(final AbsoluteDate date) { 664 return isEstimationContinuous ? getValueContinuousEstimation(date) : getValueStepEstimation(date); 665 } 666 667 /** Get current parameter value at specific date with step estimation. 668 * @param date date for which the value wants to be known. Only if 669 * parameter driver has 1 value estimated over the all orbit determination 670 * period (not validity period intervals for estimation), the date value can 671 * be <em>{@code null}</em> and then the only estimated value will be 672 * returned, in this case the date can also be whatever the value returned would 673 * be the same. Moreover in this particular case one can also call the {@link #getValue()}. 674 * @return current parameter value at date date, or for the all period if 675 * no validity period (= 1 value estimated over the all orbit determination 676 * period) 677 */ 678 public double getValueStepEstimation(final AbsoluteDate date) { 679 return getNbOfValues() == 1 ? valueSpanMap.getFirstSpan().getData() : valueSpanMap.get(date); 680 } 681 682 /** Get current parameter value at specific date with continuous estimation. 683 * @param date date for which the value wants to be known. Only if 684 * parameter driver has 1 value estimated over the all orbit determination 685 * period (not validity period intervals for estimation), the date value can 686 * be <em>{@code null}</em> and then the only estimated value will be 687 * returned, in this case the date can also be whatever the value returned would 688 * be the same. Moreover in this particular case one can also call the {@link #getValue()}. 689 * @return current parameter value at date date, or for the all period if 690 * no validity period (= 1 value estimated over the all orbit determination 691 * period) 692 * @since 12.0 693 */ 694 public double getValueContinuousEstimation(final AbsoluteDate date) { 695 //TODO 696 throw new UnsupportedOperationException(); 697 } 698 699 /** Get the value as a gradient at special date. 700 * @param freeParameters total number of free parameters in the gradient 701 * @param indices indices of the differentiation parameters in derivatives computations 702 * @return value with derivatives, will throw exception if called on a PDriver having 703 * several values driven 704 * @since 10.2 705 */ 706 public Gradient getValue(final int freeParameters, final Map<String, Integer> indices) { 707 Integer index = null; 708 for (Span<String> span = nameSpanMap.getFirstSpan(); span != null; span = span.next()) { 709 index = indices.get(span.getData()); 710 if (index != null) { 711 break; 712 } 713 } 714 return (index == null) ? Gradient.constant(freeParameters, getValue()) : Gradient.variable(freeParameters, index, getValue()); 715 } 716 717 /** Get the value as a gradient at special date. 718 * @param freeParameters total number of free parameters in the gradient 719 * @param indices indices of the differentiation parameters in derivatives computations, 720 * must be span name and not driver name 721 * @param date date for which the value wants to be known. Only if 722 * parameter driver has 1 value estimated over the all orbit determination 723 * period (not validity period intervals for estimation), the date value can 724 * be <em>{@code null}</em> and then the only estimated value will be 725 * returned 726 * @return value with derivatives 727 * @since 10.2 728 */ 729 public Gradient getValue(final int freeParameters, final Map<String, Integer> indices, final AbsoluteDate date) { 730 Integer index = null; 731 for (Span<String> span = nameSpanMap.getFirstSpan(); span != null; span = span.next()) { 732 index = indices.get(span.getData()); 733 if (index != null) { 734 break; 735 } 736 } 737 return (index == null) ? Gradient.constant(freeParameters, getValue(date)) : Gradient.variable(freeParameters, index, getValue(date)); 738 } 739 740 /** Set parameter value at specific date. 741 * <p> 742 * If {@code newValue} is below {@link #getMinValue()}, it will 743 * be silently set to {@link #getMinValue()}. If {@code newValue} is 744 * above {@link #getMaxValue()}, it will be silently set to {@link 745 * #getMaxValue()}. 746 * </p> 747 * @param date date for which the value wants to be set. Only if 748 * parameter driver has 1 value estimated over the all orbit determination 749 * period (not validity period intervals for estimation), the date value can 750 * be <em>{@code null}</em> 751 * @param newValue new value to set 752 */ 753 public void setValue(final double newValue, final AbsoluteDate date) { 754 755 double previousValue = Double.NaN; 756 AbsoluteDate referenceDateSpan = AbsoluteDate.ARBITRARY_EPOCH; 757 758 // if valid for infinity (only 1 value estimation for the orbit determination ) 759 if (getNbOfValues() == 1) { 760 previousValue = this.getValue(referenceDateSpan); 761 this.valueSpanMap = new TimeSpanMap<>(FastMath.max(minValue, FastMath.min(maxValue, newValue))); 762 // if needs to be estimated per time range / validity period 763 764 // if several value intervals 765 } else { 766 final Span<Double> valueSpan = valueSpanMap.getSpan(date); 767 previousValue = valueSpan.getData(); 768 referenceDateSpan = valueSpan.getStart(); 769 // if the Span considered is from past infinity to valueSpanEndDate it is 770 // impossible to addValidAfter past infinity because it is creating a new span that 771 // is why the below trick was set up 772 if (referenceDateSpan.equals(AbsoluteDate.PAST_INFINITY)) { 773 referenceDateSpan = valueSpan.getEnd(); 774 this.valueSpanMap.addValidBefore(FastMath.max(minValue, FastMath.min(maxValue, newValue)), 775 referenceDateSpan, false); 776 } else { 777 this.valueSpanMap.addValidAfter(FastMath.max(minValue, FastMath.min(maxValue, newValue)), 778 referenceDateSpan, false); 779 } 780 } 781 782 for (final ParameterObserver observer : observers) { 783 observer.valueChanged(previousValue, this, date); 784 } 785 } 786 787 788 /** Set parameter value. Only usable on ParameterDriver 789 * which have only 1 span on their TimeSpanMap value (that is 790 * to say for which the setPeriod method wasn't called) 791 * <p> 792 * If {@code newValue} is below {@link #getMinValue()}, it will 793 * be silently set to {@link #getMinValue()}. If {@code newValue} is 794 * above {@link #getMaxValue()}, it will be silently set to {@link 795 * #getMaxValue()}. 796 * </p> 797 * @param newValue new value to set 798 */ 799 public void setValue(final double newValue) { 800 if (getNbOfValues() == 1) { 801 final AbsoluteDate referenceDateSpan = AbsoluteDate.ARBITRARY_EPOCH; 802 final double previousValue = this.getValue(referenceDateSpan); 803 this.valueSpanMap = new TimeSpanMap<>(FastMath.max(minValue, FastMath.min(maxValue, newValue))); 804 for (final ParameterObserver observer : observers) { 805 observer.valueChanged(previousValue, this, referenceDateSpan); 806 } 807 } else { 808 throw new OrekitIllegalStateException(OrekitMessages.PARAMETER_WITH_SEVERAL_ESTIMATED_VALUES, name, "setValue(date)"); 809 } 810 } 811 812 /** Configure a parameter selection status. 813 * <p> 814 * Selection is used for estimated parameters in orbit determination, 815 * or to compute the Jacobian matrix in partial derivatives computation. 816 * </p> 817 * @param selected if true the parameter is selected, 818 * otherwise it will be fixed 819 */ 820 public void setSelected(final boolean selected) { 821 final boolean previousSelection = isSelected(); 822 this.selected = selected; 823 for (final ParameterObserver observer : observers) { 824 observer.selectionChanged(previousSelection, this); 825 } 826 } 827 828 /** Check if parameter is selected. 829 * <p> 830 * Selection is used for estimated parameters in orbit determination, 831 * or to compute the Jacobian matrix in partial derivatives computation. 832 * </p> 833 * @return true if parameter is selected, false if it is not 834 */ 835 public boolean isSelected() { 836 return selected; 837 } 838 839 /** Set parameter estimation to continuous, by default step estimation. 840 * <p> Continuous estimation : when a value wants to be known at date 841 * t, the value returned will be an interpolation between start value 842 * of the span corresponding to date t and end value (which corresponds 843 * to the start of the next span). 844 * </p> 845 * <p> Step estimation : when a value wants to be 846 * known at date t, the value returned will be the value of the beginning 847 * of span corresponding to date t, step estimation. 848 * </p> 849 * @param continuous if true the parameter will be estimated 850 * with continuous estimation, if false with step estimation. 851 */ 852 public void setContinuousEstimation(final boolean continuous) { 853 final boolean previousEstimation = isContinuousEstimation(); 854 this.isEstimationContinuous = continuous; 855 for (final ParameterObserver observer : observers) { 856 observer.estimationTypeChanged(previousEstimation, this); 857 } 858 } 859 860 /** Check if parameter estimation is continuous, that is to say when 861 * a value wants to be known at date t, the value returned 862 * will be an interpolation between start value on span corresponding 863 * for date t and end value (which corresponds to the start of the next 864 * span), continuous estimation. Or not continuous, that is to say when a value wants to be 865 * known at date t, the value returned will be the value of the start 866 * of span corresponding to date t, step estimation. 867 * @return true if continuous estimation/definition, false if step estimation/definition 868 * @since 12.0 869 */ 870 public boolean isContinuousEstimation() { 871 return isEstimationContinuous; 872 } 873 874 /** Get a text representation of the parameter. 875 * @return text representation of the parameter, in the form name = value. 876 */ 877 public String toString() { 878 return name + " = " + valueSpanMap.get(AbsoluteDate.ARBITRARY_EPOCH); 879 } 880 881 }