diff --git a/NINA.Core/Locale/Locale.resx b/NINA.Core/Locale/Locale.resx index a7f453531..63098b206 100644 --- a/NINA.Core/Locale/Locale.resx +++ b/NINA.Core/Locale/Locale.resx @@ -4459,6 +4459,15 @@ In case a longer duration is needed, a duration can be specified and the applica AF After HFR Increase + +HFR trend per filter + + +When disabled, the HFR trend is evaluated across all filters combined. +This can help to trigger autofocus runs earlier, particular when imaging with frequent filter changes +and a temperature sensitive optics on a quickly changing ambient temperature. +Note: This mode requires that HFR differences between filters in your setup are small. + A trigger to initiate an autofocus run after the temperature has changed by the given amount in degrees since the last autofocus run or start of sequence diff --git a/NINA.Sequencer/Trigger/Autofocus/AutofocusAfterHFRIncreaseTrigger.cs b/NINA.Sequencer/Trigger/Autofocus/AutofocusAfterHFRIncreaseTrigger.cs index 29aaed359..d927f9b21 100644 --- a/NINA.Sequencer/Trigger/Autofocus/AutofocusAfterHFRIncreaseTrigger.cs +++ b/NINA.Sequencer/Trigger/Autofocus/AutofocusAfterHFRIncreaseTrigger.cs @@ -120,6 +120,17 @@ public int SampleSize { } } + private bool trendPerFilter = true; // default true to keep the original behaviour creating an HFR trend per filter + + [JsonProperty] + public bool TrendPerFilter { + get => trendPerFilter; + set { + trendPerFilter = value; + RaisePropertyChanged(); + } + } + private double originalHFR; public double OriginalHFR { @@ -182,7 +193,7 @@ public override bool ShouldTrigger(ISequenceItem previousItem, ISequenceItem nex imageHistory = imageHistory.Where(point => point.Id > lastAF.Id).ToList(); } - if (fwInfo != null && fwInfo.Connected && fwInfo.SelectedFilter != null) { + if (TrendPerFilter == true && fwInfo != null && fwInfo.Connected && fwInfo.SelectedFilter != null) { //Further filter the history to only considere items by the current filter Filter = fwInfo.SelectedFilter.Name; @@ -245,7 +256,7 @@ public override bool ShouldTrigger(ISequenceItem previousItem, ISequenceItem nex } public override string ToString() { - return $"Trigger: {nameof(AutofocusAfterHFRIncreaseTrigger)}, Amount: {Amount}"; + return $"Trigger: {nameof(AutofocusAfterHFRIncreaseTrigger)}, Amount: {Amount}, TrendPerFilter: {TrendPerFilter}"; } public bool Validate() { @@ -264,4 +275,4 @@ public bool Validate() { return i.Count == 0; } } -} \ No newline at end of file +} diff --git a/NINA.Sequencer/Trigger/Datatemplates.xaml b/NINA.Sequencer/Trigger/Datatemplates.xaml index fd5f1e42e..6dae4b579 100644 --- a/NINA.Sequencer/Trigger/Datatemplates.xaml +++ b/NINA.Sequencer/Trigger/Datatemplates.xaml @@ -139,6 +139,15 @@ Text="{Binding Amount}" TextAlignment="Right" Unit="%" /> + + diff --git a/NINA.Test/Sequencer/Trigger/Autofocus/AutofocusAfterHFRIncreaseTriggerTest.cs b/NINA.Test/Sequencer/Trigger/Autofocus/AutofocusAfterHFRIncreaseTriggerTest.cs index dbf66fb0a..64e396753 100644 --- a/NINA.Test/Sequencer/Trigger/Autofocus/AutofocusAfterHFRIncreaseTriggerTest.cs +++ b/NINA.Test/Sequencer/Trigger/Autofocus/AutofocusAfterHFRIncreaseTriggerTest.cs @@ -1,4 +1,4 @@ -#region "copyright" +#region "copyright" /* Copyright © 2016 - 2024 Stefan Berg and the N.I.N.A. contributors @@ -224,7 +224,11 @@ public async Task Execute_Successfully_WithAllParametersPassedCorrectly() { public void ToString_FilledProperly() { var sut = new AutofocusAfterHFRIncreaseTrigger(profileServiceMock.Object, imagehistory, cameraMediatorMock.Object, filterWheelMediatorMock.Object, focuserMediatorMock.Object, autoFocusVMFactoryMock.Object, safetyMonitorMediatorMock.Object); var tostring = sut.ToString(); - tostring.Should().Be("Trigger: AutofocusAfterHFRIncreaseTrigger, Amount: 5"); + tostring.Should().Be("Trigger: AutofocusAfterHFRIncreaseTrigger, Amount: 5, TrendPerFilter: True"); + + sut.TrendPerFilter = false; + tostring = sut.ToString(); + tostring.Should().Be("Trigger: AutofocusAfterHFRIncreaseTrigger, Amount: 5, TrendPerFilter: False"); } [Test] @@ -263,6 +267,36 @@ public void ShouldTrigger_HistoryExists_NoPreviousAFs_OnlyTestFilterConsidered_T trigger.Should().Be(shouldTrigger); } + [Test] // index 2+3 are for a different filter + [TestCase(new double[] { 3, 3, 50, 100, 3, 3 }, 1, true, false)] // should not trigger as trend per filter true + [TestCase(new double[] { 3, 3, 50, 100, 3, 3 }, 1, false, true)] // should trigger as trend across all filters + public void ShouldTrigger_HistoryExists_NoPreviousAFs_TestTrendPerFilterConsidered(double[] hfrs, double changeAmount, bool trendPerFilter, bool shouldTrigger) { + for (int i = 0; i < hfrs.Length; i++) { + if (i > 1 && i < 4) { + var p = new ImageHistoryPoint(i, null, "LIGHT"); + var id = imagehistory.GetNextImageId(); + imagehistory.Add(id, null, "LIGHT"); + imagehistory.AppendImageProperties(new ImageSavedEventArgs() { Filter = "OtherFilter", StarDetectionAnalysis = new StarDetectionAnalysis() { DetectedStars = 1, HFR = hfrs[i] }, MetaData = new ImageMetaData() { Image = new ImageParameter() { Id = id } } }); + } else { + var p = new ImageHistoryPoint(i, null, "LIGHT"); + var id = imagehistory.GetNextImageId(); + imagehistory.Add(id, null, "LIGHT"); + imagehistory.AppendImageProperties(new ImageSavedEventArgs() { Filter = "TestFilter", StarDetectionAnalysis = new StarDetectionAnalysis() { DetectedStars = 1, HFR = hfrs[i] }, MetaData = new ImageMetaData() { Image = new ImageParameter() { Id = id } } }); + } + } + + filterWheelMediatorMock.Setup(x => x.GetInfo()).Returns(new FilterWheelInfo() { Connected = true, SelectedFilter = new FilterInfo() { Name = "TestFilter" } }); + + var sut = new AutofocusAfterHFRIncreaseTrigger(profileServiceMock.Object, imagehistory, cameraMediatorMock.Object, filterWheelMediatorMock.Object, focuserMediatorMock.Object, autoFocusVMFactoryMock.Object, safetyMonitorMediatorMock.Object); + sut.Amount = changeAmount; + sut.TrendPerFilter = trendPerFilter; + + var itemMock = new Mock(); + itemMock.SetupGet(x => x.ImageType).Returns("LIGHT"); + var trigger = sut.ShouldTrigger(null, itemMock.Object); + + trigger.Should().Be(shouldTrigger); + } [Test] @@ -328,4 +362,4 @@ public void HFRTrend_And_Percentage_AreComputed_FromWindowAndBaseline( sut.HFRTrendPercentage.Should().BeApproximately(expectedPct, 0.05); } } -} \ No newline at end of file +} diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e943777b3..cd1d6596a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -10,6 +10,8 @@ More details at n - ToupTek based filter wheels and focusers will no longer be listed in the camera connector. ## Improvements +- **Autofocus after HFR Increase Trigger** + - new Trend per Filter checkbox to consider HFR Trend per filter (default) or across all filters to earlier trigger autofocus runs when imaging with continues filter loops - When a safety monitor is connected and is reporting unsafe conditions, the imaging related core triggers will no longer fire as the conditions aren't safe anyways to execute them. - In case the meridian should trigger in this scenario, it will stop mount tracking instead to ensure there will be no pier collision. Safety related logic in a sequence needs to handle resuming tracking once it's safe again.