From 1c45794977a4f0e6622474d65b3f93d5e4844cea Mon Sep 17 00:00:00 2001 From: tiagohm Date: Thu, 18 Jul 2024 21:09:49 -0300 Subject: [PATCH] [api]: Remove Kotest in favor of JUnit --- .github/dependabot.yml | 3 + api/src/test/kotlin/APITest.kt | 219 -- .../kotlin/CalibrationFrameRepositoryTest.kt | 130 +- .../CameraCaptureNamingFormatterTest.kt | 315 ++- .../test/kotlin/PreferenceRepositoryTest.kt | 189 +- .../kotlin/SatelliteEntityRepositoryTest.kt | 68 +- .../test/kotlin/SimbadDatabaseGenerator.kt | 3 +- .../test/kotlin/SimbadEntityRepositoryTest.kt | 104 +- api/src/test/kotlin/SkyAtlasServiceTest.kt | 261 +- api/src/test/kotlin/StackerServiceTest.kt | 69 +- build.gradle.kts | 26 +- .../src/test/kotlin/QueryBuilderTest.kt | 968 ++++--- .../kotlin/ThreePointPolarAlignmentTest.kt | 129 +- .../src/test/kotlin/AlpacaServiceTest.kt | 27 +- .../kotlin/AlpacaDiscoveryProtocolTest.kt | 22 +- .../src/test/kotlin/AstrobinServiceTest.kt | 147 +- .../src/test/kotlin/LibAstrometryNetTest.kt | 192 +- .../nova/NovaAstrometryNetService.kt | 1 + .../NovaAstrometryNetPlateSolver.kt | 3 +- .../test/kotlin/AstrometryNetServiceTest.kt | 134 +- .../NovaAstrometryNetPlateSolverTest.kt | 55 +- .../src/test/kotlin/CancellationTokenTest.kt | 113 +- .../src/test/kotlin/CommandLineTest.kt | 94 +- .../src/test/kotlin/CountUpDownLatchTest.kt | 196 +- nebulosa-common/src/test/kotlin/PauserTest.kt | 43 +- .../curve/fitting/HyperbolicFitting.kt | 5 +- .../src/test/kotlin/AutoFocusTest.kt | 194 +- .../src/test/kotlin/HyperbolicFittingTest.kt | 43 +- .../src/test/kotlin/QuadraticFittingTest.kt | 29 +- .../src/test/kotlin/TrendLineFittingTest.kt | 43 +- .../src/test/kotlin/TrendLineTest.kt | 205 +- nebulosa-erfa/src/test/kotlin/ErfaTest.kt | 2437 +++++++++-------- .../src/test/kotlin/FitsFormatTest.kt | 15 +- .../kotlin/FitsHeaderCardFormatterTest.kt | 53 +- .../test/kotlin/FitsHeaderCardParserTest.kt | 95 +- nebulosa-fits/src/test/kotlin/FitsReadTest.kt | 164 +- .../src/test/kotlin/FitsWriteTest.kt | 25 +- .../src/test/kotlin/Hips2FitsServiceTest.kt | 54 +- .../src/test/kotlin/HorizonsServiceTest.kt | 211 +- .../kotlin/nebulosa/image/format/ImageHdu.kt | 35 + .../src/main/kotlin/nebulosa/image/Image.kt | 3 + .../test/kotlin/ComputationAlgorithmTest.kt | 141 +- .../test/kotlin/FitsTransformAlgorithmTest.kt | 321 ++- nebulosa-image/src/test/kotlin/HFDTest.kt | 62 +- .../test/kotlin/XisfTransformAlgorithmTest.kt | 646 +++-- .../src/test/kotlin/INDIXmlInputStreamTest.kt | 533 ++-- .../AbstractSeekableSinkAndSourceTest.kt | 583 ++-- .../src/test/kotlin/Base64InputStreamTest.kt | 19 +- .../ByteArrayWithOffsetAndLengthTest.kt | 16 +- .../test/kotlin/ByteArrayWithOffsetTest.kt | 10 +- .../ByteBufferWithOffsetAndLengthTest.kt | 16 +- .../test/kotlin/ByteBufferWithOffsetTest.kt | 10 +- ...yteBufferWrappedWithOffsetAndLengthTest.kt | 16 +- .../src/test/kotlin/RandomAccessFileTest.kt | 4 +- .../src/test/kotlin/RandomSourceTest.kt | 57 +- nebulosa-math/src/test/kotlin/AngleTest.kt | 371 +-- nebulosa-math/src/test/kotlin/DistanceTest.kt | 80 +- nebulosa-math/src/test/kotlin/Matrix3DTest.kt | 311 ++- nebulosa-math/src/test/kotlin/Vector3DTest.kt | 183 +- nebulosa-math/src/test/kotlin/VelocityTest.kt | 86 +- nebulosa-nasa/src/test/kotlin/DafTest.kt | 171 +- .../src/test/kotlin/RemoteDafTest.kt | 26 +- nebulosa-nasa/src/test/kotlin/SpkTest.kt | 73 +- .../src/test/kotlin/AstrometryTest.kt | 195 +- .../src/test/kotlin/ConstellationTest.kt | 21 +- nebulosa-nova/src/test/kotlin/ELPMPP02Test.kt | 29 +- .../src/test/kotlin/FixedStarTest.kt | 101 +- nebulosa-nova/src/test/kotlin/GUST86Test.kt | 123 +- .../src/test/kotlin/GeographicPositionTest.kt | 50 +- nebulosa-nova/src/test/kotlin/ICRFTest.kt | 86 +- .../src/test/kotlin/KeplerOrbitTest.kt | 87 +- .../src/test/kotlin/SpiceKernelTest.kt | 101 +- nebulosa-nova/src/test/kotlin/VSOP87ETest.kt | 219 +- .../src/test/kotlin/PHD2ClientTest.kt | 25 +- .../test/kotlin/PixInsightAutoStackerTest.kt | 56 +- .../test/kotlin/PixInsightPlateSolverTest.kt | 46 +- .../src/test/kotlin/PixInsightScriptTest.kt | 326 +-- .../src/test/kotlin/PixInsightStackerTest.kt | 111 +- .../test/kotlin/PixInsightStarDetectorTest.kt | 21 +- .../src/test/kotlin/PlateSolutionTest.kt | 32 +- .../SmallBodyCloseApprochServiceTest.kt | 93 +- .../SmallBodyDatabaseLookupServiceTest.kt | 76 +- .../SmallBodyIdentificationServiceTest.kt | 57 +- .../src/test/kotlin/SimbadServiceTest.kt | 224 +- nebulosa-siril/src/test/kotlin/SirilTest.kt | 108 +- .../src/test/kotlin/HygDatabaseTest.kt | 27 +- .../src/test/resources/.gitignore | 1 - .../src/test/kotlin/SaoCatalogTest.kt | 44 +- .../src/test/resources/.gitignore | 1 - .../src/test/kotlin/NebulaTest.kt | 69 +- .../src/test/kotlin/GeodesicGridTest.kt | 17 +- nebulosa-test/build.gradle.kts | 4 +- .../nebulosa/test/AbstractFitsAndXisfTest.kt | 253 -- .../main/kotlin/nebulosa/test/AbstractTest.kt | 46 + .../main/kotlin/nebulosa/test/Extensions.kt | 20 + .../src/main/kotlin/nebulosa/test/Files.kt | 109 + .../src/main/kotlin/nebulosa/test/Http.kt | 66 + .../main/kotlin/nebulosa/test/LinuxOnly.kt | 11 + .../src/main/kotlin/nebulosa/test/Matchers.kt | 4 + .../kotlin/nebulosa/test/NonGitHubOnly.kt | 10 + .../nebulosa/test/NonGitHubOnlyCondition.kt | 12 - .../src/main/kotlin/nebulosa/test/Paths.kt | 25 + .../main/kotlin/nebulosa/test/WindowsOnly.kt | 11 + nebulosa-test/src/test/kotlin/PathsTest.kt | 27 + .../src/test/kotlin/SystemPropertyTest.kt | 23 + nebulosa-time/src/test/kotlin/DeltaTTest.kt | 19 +- nebulosa-time/src/test/kotlin/IERSTest.kt | 111 +- .../src/test/kotlin/InstantOfTimeTest.kt | 77 +- .../ParabolaOfStephensonMorrison2004Test.kt | 141 +- ...laOfStephensonMorrisonHohenkerk2016Test.kt | 141 +- nebulosa-time/src/test/kotlin/S15Test.kt | 141 +- .../src/test/kotlin/TAIMinusUTCTest.kt | 17 +- ...DBMinusTTByFairheadAndBretagnon1990Test.kt | 17 +- nebulosa-time/src/test/kotlin/TimeTest.kt | 1970 ++++++------- .../src/test/kotlin/VizierServiceTest.kt | 33 +- .../src/test/kotlin/EquationsTest.kt | 31 +- .../src/test/kotlin/QuadDatabaseTest.kt | 65 +- .../src/test/kotlin/SearchStrategyTest.kt | 97 +- .../src/test/kotlin/SkySegmentSphereTest.kt | 33 +- .../src/test/kotlin/WatnetPlateSolverTest.kt | 254 +- .../src/test/kotlin/WatneyStarDetectorTest.kt | 40 +- nebulosa-wcs/src/test/kotlin/LibWCSTest.kt | 184 +- .../kotlin/CompressionByteShufflerTest.kt | 41 +- .../src/test/kotlin/XisfFormatTest.kt | 763 +++--- .../test/kotlin/XisfHeaderInputStreamTest.kt | 434 +-- settings.gradle.kts | 6 +- 126 files changed, 9863 insertions(+), 8576 deletions(-) delete mode 100644 api/src/test/kotlin/APITest.kt delete mode 100644 nebulosa-skycatalog-hyg/src/test/resources/.gitignore delete mode 100644 nebulosa-skycatalog-sao/src/test/resources/.gitignore delete mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/AbstractFitsAndXisfTest.kt create mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/AbstractTest.kt create mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/Extensions.kt create mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt create mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/Http.kt create mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/LinuxOnly.kt create mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/Matchers.kt create mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnly.kt delete mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnlyCondition.kt create mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/Paths.kt create mode 100644 nebulosa-test/src/main/kotlin/nebulosa/test/WindowsOnly.kt create mode 100644 nebulosa-test/src/test/kotlin/PathsTest.kt create mode 100644 nebulosa-test/src/test/kotlin/SystemPropertyTest.kt diff --git a/.github/dependabot.yml b/.github/dependabot.yml index abfc3e194..bc49cdc12 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -36,6 +36,9 @@ updates: apache: patterns: - "org.apache*" + junit: + patterns: + - "org.junit*" - package-ecosystem: "npm" directory: "/desktop" diff --git a/api/src/test/kotlin/APITest.kt b/api/src/test/kotlin/APITest.kt deleted file mode 100644 index fd839c066..000000000 --- a/api/src/test/kotlin/APITest.kt +++ /dev/null @@ -1,219 +0,0 @@ -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule -import com.fasterxml.jackson.module.kotlin.kotlinModule -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.booleans.shouldBeTrue -import kotlinx.coroutines.delay -import nebulosa.api.autofocus.AutoFocusRequest -import nebulosa.api.beans.converters.time.DurationSerializer -import nebulosa.api.cameras.CameraStartCaptureRequest -import nebulosa.api.connection.ConnectionType -import nebulosa.api.stardetector.StarDetectionRequest -import nebulosa.common.json.PathSerializer -import nebulosa.test.AbstractFitsAndXisfTest.Companion.HTTP_CLIENT -import nebulosa.test.NonGitHubOnlyCondition -import okhttp3.MediaType.Companion.toMediaType -import okhttp3.Request -import okhttp3.RequestBody -import okhttp3.RequestBody.Companion.toRequestBody -import java.nio.file.Path -import java.time.Duration - -@EnabledIf(NonGitHubOnlyCondition::class) -class APITest : StringSpec() { - - init { - // GENERAL. - - "Connect" { connect() } - "Disconnect" { disconnect() } - - // CAMERA. - - "Cameras" { cameras() } - "Camera Connect" { cameraConnect() } - "Camera" { camera() } - "Camera Capture Start" { cameraStartCapture() } - "Camera Capture Stop" { cameraStopCapture() } - "Camera Disconnect" { cameraDisconnect() } - - // MOUNT. - - "Mounts" { mounts() } - "Mount Connect" { mountConnect() } - "Mount" { mount() } - "Mount Remote Control Start" { mountRemoteControlStart() } - "Mount Remote Control List" { mountRemoteControlList() } - "Mount Remote Control Stop" { mountRemoteControlStop() } - "Mount Disconnect" { mountDisconnect() } - - // FOCUSER. - - "Focusers" { focusers() } - "Focuser Connect" { focuserConnect() } - "Focuser" { focuser() } - "Focuser Disconnect" { focuserDisconnect() } - - // AUTO FOCUS. - - "Auto Focus Start" { - connect("192.168.31.153", 11111, ConnectionType.ALPACA) - delay(1000) - cameraConnect() - focuserConnect() - delay(1000) - // focuserMoveTo(position = 8100) - delay(2000) - autoFocusStart() - } - "Auto Focus Stop" { autoFocusStop() } - } - - private fun connect(host: String = "0.0.0.0", port: Int = 7624, type: ConnectionType = ConnectionType.INDI) { - put("connection?host=$host&port=$port&type=$type") - } - - private fun disconnect() { - delete("connection") - } - - private fun cameras() { - get("cameras") - } - - private fun cameraConnect(camera: String = CAMERA_NAME) { - put("cameras/$camera/connect") - } - - private fun cameraDisconnect(camera: String = CAMERA_NAME) { - put("cameras/$camera/disconnect") - } - - private fun camera(camera: String = CAMERA_NAME) { - get("cameras/$camera") - } - - private fun cameraStartCapture(camera: String = CAMERA_NAME) { - putJson("cameras/$camera/capture/start", CAMERA_START_CAPTURE_REQUEST) - } - - private fun cameraStopCapture(camera: String = CAMERA_NAME) { - put("cameras/$camera/capture/abort") - } - - private fun mounts() { - get("mounts") - } - - private fun mountConnect(mount: String = MOUNT_NAME) { - put("mounts/$mount/connect") - } - - private fun mountDisconnect(mount: String = MOUNT_NAME) { - put("mounts/$mount/disconnect") - } - - private fun mount(mount: String = MOUNT_NAME) { - get("mounts/$mount") - } - - private fun mountRemoteControlStart(mount: String = MOUNT_NAME, host: String = "0.0.0.0", port: Int = 10001) { - put("mounts/$mount/remote-control/start?type=LX200&host=$host&port=$port") - } - - private fun mountRemoteControlList(mount: String = MOUNT_NAME) { - get("mounts/$mount/remote-control") - } - - private fun mountRemoteControlStop(mount: String = MOUNT_NAME) { - put("mounts/$mount/remote-control/stop?type=LX200") - } - - private fun focusers() { - get("focusers") - } - - private fun focuserConnect(focuser: String = FOCUSER_NAME) { - put("focusers/$focuser/connect") - } - - private fun focuserDisconnect(focuser: String = FOCUSER_NAME) { - put("focusers/$focuser/disconnect") - } - - private fun focuser(focuser: String = FOCUSER_NAME) { - get("focusers/$focuser") - } - - private fun focuserMoveTo(focuser: String = FOCUSER_NAME, position: Int) { - put("focusers/$focuser/move-to?steps=$position") - } - - private fun autoFocusStart(camera: String = CAMERA_NAME, focuser: String = FOCUSER_NAME) { - putJson("auto-focus/$camera/$focuser/start", AUTO_FOCUS_REQUEST) - } - - private fun autoFocusStop(camera: String = CAMERA_NAME) { - put("auto-focus/$camera/stop") - } - - companion object { - - private const val BASE_URL = "http://localhost:7000" - private const val CAMERA_NAME = "Sky Simulator" - private const val MOUNT_NAME = "Telescope Simulator" - private const val FOCUSER_NAME = "ZWO Focuser (1)" - - @JvmStatic private val EXPOSURE_TIME = Duration.ofSeconds(5) - @JvmStatic private val CAPTURES_PATH = Path.of(System.getProperty("user.home"), "/Git/nebulosa/data/captures") - - @JvmStatic private val STAR_DETECTION_OPTIONS = StarDetectionRequest(executablePath = Path.of("astap")) - - @JvmStatic private val CAMERA_START_CAPTURE_REQUEST = CameraStartCaptureRequest( - exposureTime = EXPOSURE_TIME, width = 1280, height = 1024, frameFormat = "INDI_MONO", - savePath = CAPTURES_PATH, exposureAmount = 1 - ) - - @JvmStatic private val AUTO_FOCUS_REQUEST = AutoFocusRequest( - capture = CAMERA_START_CAPTURE_REQUEST, stepSize = 500, - starDetector = STAR_DETECTION_OPTIONS - ) - - @JvmStatic private val KOTLIN_MODULE = kotlinModule() - .addSerializer(PathSerializer) - .addSerializer(DurationSerializer()) - - @JvmStatic private val OBJECT_MAPPER = ObjectMapper() - .registerModule(JavaTimeModule()) - .registerModule(KOTLIN_MODULE) - - @JvmStatic private val APPLICATION_JSON = "application/json".toMediaType() - @JvmStatic private val EMPTY_BODY = ByteArray(0).toRequestBody(APPLICATION_JSON) - - @JvmStatic - private fun get(path: String) { - val request = Request.Builder().get().url("$BASE_URL/$path").build() - HTTP_CLIENT.newCall(request).execute().use { it.isSuccessful.shouldBeTrue() } - } - - @JvmStatic - private fun put(path: String, body: RequestBody = EMPTY_BODY) { - val request = Request.Builder().put(body).url("$BASE_URL/$path").build() - HTTP_CLIENT.newCall(request).execute().use { it.isSuccessful.shouldBeTrue() } - } - - @JvmStatic - private fun putJson(path: String, data: Any) { - val bytes = OBJECT_MAPPER.writeValueAsBytes(data) - val body = bytes.toRequestBody(APPLICATION_JSON) - put(path, body) - } - - @JvmStatic - private fun delete(path: String) { - val request = Request.Builder().delete().url("$BASE_URL/$path").build() - HTTP_CLIENT.newCall(request).execute().use { it.isSuccessful.shouldBeTrue() } - } - } -} diff --git a/api/src/test/kotlin/CalibrationFrameRepositoryTest.kt b/api/src/test/kotlin/CalibrationFrameRepositoryTest.kt index 8cdd27d80..602571baa 100644 --- a/api/src/test/kotlin/CalibrationFrameRepositoryTest.kt +++ b/api/src/test/kotlin/CalibrationFrameRepositoryTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldHaveSize import io.objectbox.kotlin.boxFor @@ -6,84 +5,91 @@ import nebulosa.api.calibration.CalibrationFrameEntity import nebulosa.api.calibration.CalibrationFrameRepository import nebulosa.api.database.MyObjectBox import nebulosa.indi.device.camera.FrameType +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Test import java.util.* -class CalibrationFrameRepositoryTest : StringSpec() { +class CalibrationFrameRepositoryTest { - init { - val boxStore = MyObjectBox.builder() - .inMemory(UUID.randomUUID().toString()) - .build() - - afterSpec { - boxStore.close() - } + @Test + fun findAll() { + REPOSITORY.findAll().shouldHaveSize(17) + } - val box = boxStore.boxFor() - val repository = CalibrationFrameRepository(box) + @Test + fun findDarks() { + REPOSITORY.darkFrames(NAME, 1280, 1024, 1, 1L, 0.0).shouldHaveSize(1) + REPOSITORY.darkFrames(NAME, 1280, 1024, 1, 60L, 0.0).shouldHaveSize(4) + REPOSITORY.darkFrames(NAME, 1280, 1024, 1, 60L, 100.0).shouldHaveSize(2) + REPOSITORY.darkFrames(NAME, 1280, 1024, 1, 60L, 50.0).shouldBeEmpty() + REPOSITORY.darkFrames(NAME, 1280, 1024, 2, 60L, 100.0).shouldBeEmpty() + REPOSITORY.darkFrames(NAME, 4092, 2800, 1, 60L, 100.0).shouldBeEmpty() + REPOSITORY.darkFrames("ZW", 1280, 1024, 1, 1L, 0.0).shouldBeEmpty() + } - repository.save(FrameType.DARK, 1L) - repository.save(FrameType.DARK, 2L) - repository.save(FrameType.DARK, 5L) - repository.save(FrameType.DARK, 10L) - repository.save(FrameType.DARK, 30L) - repository.save(FrameType.DARK, 60L) - repository.save(FrameType.DARK, 60L, gain = 100.0) - repository.save(FrameType.DARK, 10L, temperature = -10.0) - repository.save(FrameType.DARK, 30L, temperature = -10.0) - repository.save(FrameType.DARK, 60L, temperature = -10.0) - repository.save(FrameType.DARK, 60L, temperature = -10.0, gain = 100.0) - repository.save(FrameType.BIAS, 0L) - repository.save(FrameType.BIAS, 0L, gain = 100.0) - repository.save(FrameType.FLAT, 0L, filter = "RED") - repository.save(FrameType.FLAT, 0L, filter = "GREEN") - repository.save(FrameType.FLAT, 0L, filter = "BLUE") - repository.save(FrameType.FLAT, 0L, filter = null) + @Test + fun findBias() { + REPOSITORY.biasFrames(NAME, 1280, 1024, 1, 0.0).shouldHaveSize(2) + REPOSITORY.biasFrames(NAME, 1280, 1024, 1, 100.0).shouldHaveSize(1) + REPOSITORY.biasFrames(NAME, 1280, 1024, 1, 50.0).shouldBeEmpty() + REPOSITORY.biasFrames(NAME, 1280, 1024, 2, 0.0).shouldBeEmpty() + REPOSITORY.biasFrames(NAME, 4092, 2800, 1, 0.0).shouldBeEmpty() + REPOSITORY.biasFrames("ZW", 1280, 1024, 2, 0.0).shouldBeEmpty() + } - "find all" { - repository.findAll().shouldHaveSize(17) - } - "find darks" { - repository.darkFrames(NAME, 1280, 1024, 1, 1L, 0.0).shouldHaveSize(1) - repository.darkFrames(NAME, 1280, 1024, 1, 60L, 0.0).shouldHaveSize(4) - repository.darkFrames(NAME, 1280, 1024, 1, 60L, 100.0).shouldHaveSize(2) - repository.darkFrames(NAME, 1280, 1024, 1, 60L, 50.0).shouldBeEmpty() - repository.darkFrames(NAME, 1280, 1024, 2, 60L, 100.0).shouldBeEmpty() - repository.darkFrames(NAME, 4092, 2800, 1, 60L, 100.0).shouldBeEmpty() - repository.darkFrames("ZW", 1280, 1024, 1, 1L, 0.0).shouldBeEmpty() - } - "find bias" { - repository.biasFrames(NAME, 1280, 1024, 1, 0.0).shouldHaveSize(2) - repository.biasFrames(NAME, 1280, 1024, 1, 100.0).shouldHaveSize(1) - repository.biasFrames(NAME, 1280, 1024, 1, 50.0).shouldBeEmpty() - repository.biasFrames(NAME, 1280, 1024, 2, 0.0).shouldBeEmpty() - repository.biasFrames(NAME, 4092, 2800, 1, 0.0).shouldBeEmpty() - repository.biasFrames("ZW", 1280, 1024, 2, 0.0).shouldBeEmpty() - } - "find flats" { - repository.flatFrames(NAME, null, 1280, 1024, 1).shouldHaveSize(1) - repository.flatFrames(NAME, "RED", 1280, 1024, 1).shouldHaveSize(1) - repository.flatFrames(NAME, "green", 1280, 1024, 1).shouldHaveSize(1) - repository.flatFrames(NAME, "BLUE", 1280, 1024, 1).shouldHaveSize(1) - repository.flatFrames(NAME, "RED", 1280, 1024, 2).shouldBeEmpty() - repository.flatFrames(NAME, "RED", 4092, 2800, 2).shouldBeEmpty() - repository.flatFrames(NAME, "HA", 1280, 1024, 2).shouldBeEmpty() - repository.flatFrames("ZW", "RED", 1280, 1024, 2).shouldBeEmpty() - } + @Test + fun findFlats() { + REPOSITORY.flatFrames(NAME, null, 1280, 1024, 1).shouldHaveSize(1) + REPOSITORY.flatFrames(NAME, "RED", 1280, 1024, 1).shouldHaveSize(1) + REPOSITORY.flatFrames(NAME, "green", 1280, 1024, 1).shouldHaveSize(1) + REPOSITORY.flatFrames(NAME, "BLUE", 1280, 1024, 1).shouldHaveSize(1) + REPOSITORY.flatFrames(NAME, "RED", 1280, 1024, 2).shouldBeEmpty() + REPOSITORY.flatFrames(NAME, "RED", 4092, 2800, 2).shouldBeEmpty() + REPOSITORY.flatFrames(NAME, "HA", 1280, 1024, 2).shouldBeEmpty() + REPOSITORY.flatFrames("ZW", "RED", 1280, 1024, 2).shouldBeEmpty() } companion object { private const val NAME = "CCD Simulator" + @JvmStatic private val BOX_STORE = MyObjectBox.builder() + .inMemory(UUID.randomUUID().toString()) + .build() + + @AfterAll + @JvmStatic + fun closeBoxStore() { + BOX_STORE.close() + } + + @JvmStatic private val BOX = BOX_STORE.boxFor() + @JvmStatic private val REPOSITORY = CalibrationFrameRepository(BOX).apply { + save(FrameType.DARK, 1L) + save(FrameType.DARK, 2L) + save(FrameType.DARK, 5L) + save(FrameType.DARK, 10L) + save(FrameType.DARK, 30L) + save(FrameType.DARK, 60L) + save(FrameType.DARK, 60L, gain = 100.0) + save(FrameType.DARK, 10L, temperature = -10.0) + save(FrameType.DARK, 30L, temperature = -10.0) + save(FrameType.DARK, 60L, temperature = -10.0) + save(FrameType.DARK, 60L, temperature = -10.0, gain = 100.0) + save(FrameType.BIAS, 0L) + save(FrameType.BIAS, 0L, gain = 100.0) + save(FrameType.FLAT, 0L, filter = "RED") + save(FrameType.FLAT, 0L, filter = "GREEN") + save(FrameType.FLAT, 0L, filter = "BLUE") + save(FrameType.FLAT, 0L, filter = null) + } + @JvmStatic internal fun CalibrationFrameRepository.save( type: FrameType, exposureTime: Long, temperature: Double = 25.0, width: Int = 1280, height: Int = 1024, bin: Int = 1, gain: Double = 0.0, filter: String? = null, - ) { - save(CalibrationFrameEntity(0L, type, NAME, filter, exposureTime, temperature, width, height, bin, bin, gain)) - } + ) = save(CalibrationFrameEntity(0L, type, NAME, filter, exposureTime, temperature, width, height, bin, bin, gain)) } } diff --git a/api/src/test/kotlin/CameraCaptureNamingFormatterTest.kt b/api/src/test/kotlin/CameraCaptureNamingFormatterTest.kt index 645d7a7f0..cc0c6660f 100644 --- a/api/src/test/kotlin/CameraCaptureNamingFormatterTest.kt +++ b/api/src/test/kotlin/CameraCaptureNamingFormatterTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import nebulosa.api.cameras.CameraCaptureNamingFormatter import nebulosa.api.cameras.CameraCaptureNamingFormatter.Companion.BIAS_FORMAT @@ -21,16 +20,15 @@ import nebulosa.indi.device.rotator.Rotator import nebulosa.indi.protocol.INDIProtocol import nebulosa.indi.protocol.PropertyState import nebulosa.math.* +import org.junit.jupiter.api.Test import java.time.* import java.util.* -class CameraCaptureNamingFormatterTest : StringSpec() { +class CameraCaptureNamingFormatterTest { - init { - val header = FitsHeader() - val clock = Clock.fixed(Instant.ofEpochSecond(1720114656, 369000000), ZoneOffset.UTC) - val formatter = CameraCaptureNamingFormatter(CameraSim, MountSim, WheelSim, FocuserSim, RotatorSim, clock) + private val header = FitsHeader() + init { header.add("FRAME", "Light") header.add(FitsKeyword.EXPTIME, 1.5) header.add(FitsKeyword.FILTER, "Red") @@ -41,134 +39,193 @@ class CameraCaptureNamingFormatterTest : StringSpec() { header.add(FitsKeyword.CCD_TEMP, -15.0) header.add(FitsKeyword.RA, "06 45 08.91728".hours.toDegrees) header.add(FitsKeyword.DEC, "-16 42 58.0171".deg.toDegrees) + } - "type" { - formatter.format("[type]", header) shouldBe "LIGHT" - } - "year" { - formatter.format("[year]", header) shouldBe "2024" - formatter.format("[year:2]", header) shouldBe "24" - formatter.format("[year:4]", header) shouldBe "2024" - formatter.format("[year:3]", header) shouldBe "2024" - } - "month" { - formatter.format("[month]", header) shouldBe "07" - } - "day" { - formatter.format("[day]", header) shouldBe "04" - } - "hour" { - formatter.format("[hour]", header) shouldBe "17" - } - "minute" { - formatter.format("[min]", header) shouldBe "37" - formatter.format("[minute]", header) shouldBe "37" - } - "second" { - formatter.format("[sec]", header) shouldBe "36" - formatter.format("[second]", header) shouldBe "36" - } - "millisecond" { - formatter.format("[ms]", header) shouldBe "369" - } - "exposure time" { - formatter.format("[exp]", header) shouldBe "1500000" - formatter.format("[exp:s]", header) shouldBe "1s" - formatter.format("[exp:ms]", header) shouldBe "1500ms" - formatter.format("[exp:us]", header) shouldBe "1500000" - formatter.format("[exposure]", header) shouldBe "1500000" - } - "filter" { - formatter.format("[filter]", header) shouldBe "Red" - } - "gain" { - formatter.format("[gain]", header) shouldBe "80" - } - "bin" { - formatter.format("[bin]", header) shouldBe "2" - } - "width" { - formatter.format("[width]", header) shouldBe "1280" - formatter.format("[w]", header) shouldBe "1280" - } - "height" { - formatter.format("[height]", header) shouldBe "1024" - formatter.format("[h]", header) shouldBe "1024" - } - "temperature" { - formatter.format("[temp]", header) shouldBe "-15" - formatter.format("[temperature]", header) shouldBe "-15" - } - "right ascension" { - formatter.format("[ra]", header) shouldBe "06h45m09s" - } - "declination" { - formatter.format("[dec]", header) shouldBe "-016d42m58s" - } - "camera" { - formatter.format("[camera]", header) shouldBe "Camera Simulator" - } - "mount" { - formatter.format("[mount]", header) shouldBe "Mount Simulator" - } - "focuser" { - formatter.format("[focuser]", header) shouldBe "Focuser Simulator" - } - "wheel" { - formatter.format("[wheel]", header) shouldBe "Wheel Simulator" - } - "rotator" { - formatter.format("[rotator]", header) shouldBe "Rotator Simulator" - } - "n" { - formatter.format("[n]", header) shouldBe "0001" - formatter.format("[n:1]", header) shouldBe "2" - formatter.format("[n:2]", header) shouldBe "03" - formatter.format("[n:3]", header) shouldBe "004" - formatter.format("[n:6]", header) shouldBe "000005" - } - "light" { - with(header.clone()) { - add("FRAME", "Light") - formatter.format(LIGHT_FORMAT, this) shouldBe "Camera Simulator_LIGHT_240704173736369_Red_1280_1024_1500000_2_80" - } - } - "dark" { - with(header.clone()) { - add("FRAME", "Dark") - formatter.format(DARK_FORMAT, this) shouldBe "Camera Simulator_DARK_1280_1024_1500000_2_80" - } - } - "flat" { - with(header.clone()) { - add("FRAME", "Flat") - formatter.format(FLAT_FORMAT, this) shouldBe "Camera Simulator_FLAT_Red_1280_1024_2" - } + @Test + fun type() { + FORMATTER.format("[type]", header) shouldBe "LIGHT" + } + + @Test + fun year() { + FORMATTER.format("[year]", header) shouldBe "2024" + FORMATTER.format("[year:2]", header) shouldBe "24" + FORMATTER.format("[year:4]", header) shouldBe "2024" + FORMATTER.format("[year:3]", header) shouldBe "2024" + } + + @Test + fun month() { + FORMATTER.format("[month]", header) shouldBe "07" + } + + @Test + fun day() { + FORMATTER.format("[day]", header) shouldBe "04" + } + + @Test + fun hour() { + FORMATTER.format("[hour]", header) shouldBe "17" + } + + @Test + fun minute() { + FORMATTER.format("[min]", header) shouldBe "37" + FORMATTER.format("[minute]", header) shouldBe "37" + } + + @Test + fun second() { + FORMATTER.format("[sec]", header) shouldBe "36" + FORMATTER.format("[second]", header) shouldBe "36" + } + + @Test + fun millisecond() { + FORMATTER.format("[ms]", header) shouldBe "369" + } + + @Test + fun exposureTime() { + FORMATTER.format("[exp]", header) shouldBe "1500000" + FORMATTER.format("[exp:s]", header) shouldBe "1s" + FORMATTER.format("[exp:ms]", header) shouldBe "1500ms" + FORMATTER.format("[exp:us]", header) shouldBe "1500000" + FORMATTER.format("[exposure]", header) shouldBe "1500000" + } + + @Test + fun filter() { + FORMATTER.format("[filter]", header) shouldBe "Red" + } + + @Test + fun gain() { + FORMATTER.format("[gain]", header) shouldBe "80" + } + + @Test + fun bin() { + FORMATTER.format("[bin]", header) shouldBe "2" + } + + @Test + fun width() { + FORMATTER.format("[width]", header) shouldBe "1280" + FORMATTER.format("[w]", header) shouldBe "1280" + } + + @Test + fun height() { + FORMATTER.format("[height]", header) shouldBe "1024" + FORMATTER.format("[h]", header) shouldBe "1024" + } + + @Test + fun temperature() { + FORMATTER.format("[temp]", header) shouldBe "-15" + FORMATTER.format("[temperature]", header) shouldBe "-15" + } + + @Test + fun rightAscension() { + FORMATTER.format("[ra]", header) shouldBe "06h45m09s" + } + + @Test + fun declination() { + FORMATTER.format("[dec]", header) shouldBe "-016d42m58s" + } + + @Test + fun camera() { + FORMATTER.format("[camera]", header) shouldBe "Camera Simulator" + } + + @Test + fun mount() { + FORMATTER.format("[mount]", header) shouldBe "Mount Simulator" + } + + @Test + fun focuser() { + FORMATTER.format("[focuser]", header) shouldBe "Focuser Simulator" + } + + @Test + fun wheel() { + FORMATTER.format("[wheel]", header) shouldBe "Wheel Simulator" + } + + @Test + fun rotator() { + FORMATTER.format("[rotator]", header) shouldBe "Rotator Simulator" + } + + @Test + fun n() { + FORMATTER.format("[n]", header) shouldBe "0001" + FORMATTER.format("[n:1]", header) shouldBe "2" + FORMATTER.format("[n:2]", header) shouldBe "03" + FORMATTER.format("[n:3]", header) shouldBe "004" + FORMATTER.format("[n:6]", header) shouldBe "000005" + } + + @Test + fun light() { + with(header) { + add("FRAME", "Light") + FORMATTER.format(LIGHT_FORMAT, this) shouldBe "Camera Simulator_LIGHT_240704173736369_Red_1280_1024_1500000_2_80" } - "bias" { - with(header.clone()) { - add("FRAME", "Bias") - formatter.format(BIAS_FORMAT, this) shouldBe "Camera Simulator_BIAS_1280_1024_2_80" - } + } + + @Test + fun dark() { + with(header) { + add("FRAME", "Dark") + FORMATTER.format(DARK_FORMAT, this) shouldBe "Camera Simulator_DARK_1280_1024_1500000_2_80" } - "unknown" { - formatter.format("[abc]_[camera]_[123]", header) shouldBe "abc_Camera Simulator_123" + } + + @Test + fun flat() { + with(header) { + add("FRAME", "Flat") + FORMATTER.format(FLAT_FORMAT, this) shouldBe "Camera Simulator_FLAT_Red_1280_1024_2" } - "not found" { - with(header.clone()) { - delete(FitsKeyword.RA) - formatter.format("[ra]", this) shouldBe "" - formatter.format("[ra]_[ra]", this) shouldBe "_" - } + } + + @Test + fun bias() { + with(header) { + add("FRAME", "Bias") + FORMATTER.format(BIAS_FORMAT, this) shouldBe "Camera Simulator_BIAS_1280_1024_2_80" } - "illegal chars" { - formatter.format("[???]", header) shouldBe "[]" - formatter.format("[???a]", header) shouldBe "[a]" - formatter.format("[a???]", header) shouldBe "[a]" - formatter.format("[][/\\:*?\"<>|]", header) shouldBe "[][]" + } + + @Test + fun unknown() { + FORMATTER.format("[abc]_[camera]_[123]", header) shouldBe "abc_Camera Simulator_123" + } + + @Test + fun notFound() { + with(header) { + delete(FitsKeyword.RA) + FORMATTER.format("[ra]", this) shouldBe "" + FORMATTER.format("[ra]_[ra]", this) shouldBe "_" } } + @Test + fun illegalChars() { + FORMATTER.format("[???]", header) shouldBe "[]" + FORMATTER.format("[???a]", header) shouldBe "[a]" + FORMATTER.format("[a???]", header) shouldBe "[a]" + FORMATTER.format("[][/\\:*?\"<>|]", header) shouldBe "[][]" + } + @Suppress("LeakingThis") private abstract class Simulator(override val name: String) : Device, MessageSender { @@ -416,4 +473,10 @@ class CameraCaptureNamingFormatterTest : StringSpec() { override fun abortRotator() = Unit } + + companion object { + + @JvmStatic private val CLOCK = Clock.fixed(Instant.ofEpochSecond(1720114656, 369000000), ZoneOffset.UTC) + @JvmStatic private val FORMATTER = CameraCaptureNamingFormatter(CameraSim, MountSim, WheelSim, FocuserSim, RotatorSim, CLOCK) + } } diff --git a/api/src/test/kotlin/PreferenceRepositoryTest.kt b/api/src/test/kotlin/PreferenceRepositoryTest.kt index 81bb5d59f..8e68479e9 100644 --- a/api/src/test/kotlin/PreferenceRepositoryTest.kt +++ b/api/src/test/kotlin/PreferenceRepositoryTest.kt @@ -1,5 +1,4 @@ import com.fasterxml.jackson.module.kotlin.jsonMapper -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.doubles.shouldBeExactly @@ -15,97 +14,117 @@ import nebulosa.api.preference.PreferenceEntity import nebulosa.api.preference.PreferenceRepository import nebulosa.api.preference.PreferenceService import nebulosa.indi.device.camera.FrameType +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Test import java.util.* -class PreferenceRepositoryTest : StringSpec() { +class PreferenceRepositoryTest { - init { - val boxStore = MyObjectBox.builder() + @Test + fun boolean() { + SERVICE.contains("b").shouldBeFalse() + SERVICE.putBoolean("b", true) + SERVICE.contains("b").shouldBeTrue() + SERVICE.getBoolean("b").shouldNotBeNull().shouldBeTrue() + SERVICE.putBoolean("b", false) + SERVICE.getBoolean("b").shouldNotBeNull().shouldBeFalse() + SERVICE.delete("b") + SERVICE.contains("b").shouldBeFalse() + SERVICE.getBoolean("b").shouldBeNull() + } + + @Test + fun int() { + SERVICE.contains("i").shouldBeFalse() + SERVICE.putInt("i", 22) + SERVICE.contains("i").shouldBeTrue() + SERVICE.getInt("i").shouldNotBeNull() shouldBeExactly 22 + SERVICE.delete("i") + SERVICE.contains("i").shouldBeFalse() + SERVICE.getInt("i").shouldBeNull() + } + + @Test + fun long() { + SERVICE.contains("l").shouldBeFalse() + SERVICE.putLong("l", 22L) + SERVICE.contains("l").shouldBeTrue() + SERVICE.getLong("l").shouldNotBeNull() shouldBeExactly 22L + SERVICE.delete("l") + SERVICE.contains("l").shouldBeFalse() + SERVICE.getLong("l").shouldBeNull() + } + + @Test + fun double() { + SERVICE.contains("d").shouldBeFalse() + SERVICE.putDouble("d", 22.0) + SERVICE.contains("d").shouldBeTrue() + SERVICE.getDouble("d").shouldNotBeNull() shouldBeExactly 22.0 + SERVICE.delete("d") + SERVICE.contains("d").shouldBeFalse() + SERVICE.getDouble("d").shouldBeNull() + } + + @Test + fun text() { + SERVICE.contains("s").shouldBeFalse() + SERVICE.putText("s", "Texto") + SERVICE.contains("s").shouldBeTrue() + SERVICE.getText("s").shouldNotBeNull() shouldBe "Texto" + SERVICE.delete("s") + SERVICE.contains("s").shouldBeFalse() + SERVICE.getText("s").shouldBeNull() + } + + @Test + fun enum() { + SERVICE.contains("e").shouldBeFalse() + SERVICE.putEnum("e", FrameType.DARK) + SERVICE.contains("e").shouldBeTrue() + SERVICE.getEnum("e").shouldNotBeNull() shouldBe FrameType.DARK + SERVICE.delete("e") + SERVICE.contains("e").shouldBeFalse() + SERVICE.getEnum("e").shouldBeNull() + } + + @Test + fun json() { + SERVICE.contains("j").shouldBeFalse() + SERVICE.putJSON("j", Location(longitude = 123.456)) + SERVICE.contains("j").shouldBeTrue() + SERVICE.getJSON("j").shouldNotBeNull() shouldBe Location(longitude = 123.456) + SERVICE.delete("j") + SERVICE.contains("j").shouldBeFalse() + SERVICE.getJSON("j").shouldBeNull() + } + + @Test + fun clear() { + SERVICE.putLong("l", 22L) + SERVICE.putDouble("d", 22.0) + SERVICE.putText("s", "Texto") + SERVICE.putEnum("e", FrameType.DARK) + SERVICE.putJSON("j", Location(longitude = 123.456)) + SERVICE.size shouldBeExactly 5 + SERVICE.clear() + SERVICE.isEmpty().shouldBeTrue() + } + + companion object { + + @JvmStatic private val BOX_STORE = MyObjectBox.builder() .inMemory(UUID.randomUUID().toString()) .build() - afterSpec { - boxStore.close() + @AfterAll + @JvmStatic + fun closeBoxStore() { + BOX_STORE.close() } - val box = boxStore.boxFor() - val repository = PreferenceRepository(box) - val service = PreferenceService(repository, jsonMapper { }) - - "boolean" { - service.contains("b").shouldBeFalse() - service.putBoolean("b", true) - service.contains("b").shouldBeTrue() - service.getBoolean("b").shouldNotBeNull().shouldBeTrue() - service.putBoolean("b", false) - service.getBoolean("b").shouldNotBeNull().shouldBeFalse() - service.delete("b") - service.contains("b").shouldBeFalse() - service.getBoolean("b").shouldBeNull() - } - "int" { - service.contains("i").shouldBeFalse() - service.putInt("i", 22) - service.contains("i").shouldBeTrue() - service.getInt("i").shouldNotBeNull() shouldBeExactly 22 - service.delete("i") - service.contains("i").shouldBeFalse() - service.getInt("i").shouldBeNull() - } - "long" { - service.contains("l").shouldBeFalse() - service.putLong("l", 22L) - service.contains("l").shouldBeTrue() - service.getLong("l").shouldNotBeNull() shouldBeExactly 22L - service.delete("l") - service.contains("l").shouldBeFalse() - service.getLong("l").shouldBeNull() - } - "double" { - service.contains("d").shouldBeFalse() - service.putDouble("d", 22.0) - service.contains("d").shouldBeTrue() - service.getDouble("d").shouldNotBeNull() shouldBeExactly 22.0 - service.delete("d") - service.contains("d").shouldBeFalse() - service.getDouble("d").shouldBeNull() - } - "text" { - service.contains("s").shouldBeFalse() - service.putText("s", "Texto") - service.contains("s").shouldBeTrue() - service.getText("s").shouldNotBeNull() shouldBe "Texto" - service.delete("s") - service.contains("s").shouldBeFalse() - service.getText("s").shouldBeNull() - } - "enum" { - service.contains("e").shouldBeFalse() - service.putEnum("e", FrameType.DARK) - service.contains("e").shouldBeTrue() - service.getEnum("e").shouldNotBeNull() shouldBe FrameType.DARK - service.delete("e") - service.contains("e").shouldBeFalse() - service.getEnum("e").shouldBeNull() - } - "json" { - service.contains("j").shouldBeFalse() - service.putJSON("j", Location(longitude = 123.456)) - service.contains("j").shouldBeTrue() - service.getJSON("j").shouldNotBeNull() shouldBe Location(longitude = 123.456) - service.delete("j") - service.contains("j").shouldBeFalse() - service.getJSON("j").shouldBeNull() - } - "clear" { - service.putLong("l", 22L) - service.putDouble("d", 22.0) - service.putText("s", "Texto") - service.putEnum("e", FrameType.DARK) - service.putJSON("j", Location(longitude = 123.456)) - service.size shouldBeExactly 5 - service.clear() - service.isEmpty().shouldBeTrue() - } + @JvmStatic private val BOX = BOX_STORE.boxFor() + @JvmStatic private val REPOSITORY = PreferenceRepository(BOX) + @JvmStatic private val SERVICE = PreferenceService(REPOSITORY, jsonMapper { }) } } diff --git a/api/src/test/kotlin/SatelliteEntityRepositoryTest.kt b/api/src/test/kotlin/SatelliteEntityRepositoryTest.kt index 3450687ef..d667bf606 100644 --- a/api/src/test/kotlin/SatelliteEntityRepositoryTest.kt +++ b/api/src/test/kotlin/SatelliteEntityRepositoryTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldHaveSize import io.objectbox.kotlin.boxFor @@ -6,41 +5,34 @@ import nebulosa.api.atlas.SatelliteEntity import nebulosa.api.atlas.SatelliteGroupType import nebulosa.api.atlas.SatelliteRepository import nebulosa.api.database.MyObjectBox +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Test import java.util.* -class SatelliteEntityRepositoryTest : StringSpec() { +class SatelliteEntityRepositoryTest { - init { - val boxStore = MyObjectBox.builder() - .inMemory(UUID.randomUUID().toString()) - .build() - - afterSpec { - boxStore.close() - } + @Test + fun findAll() { + REPOSITORY.search().shouldHaveSize(2) + } - val box = boxStore.boxFor() - val repository = SatelliteRepository(box) + @Test + fun findByName() { + REPOSITORY.search("iss").shouldHaveSize(1) + } - repository.save("ISS (ZARYA)", ISS_TLE, SatelliteGroupType.ACTIVE, SatelliteGroupType.EDUCATION) - repository.save("StarLink", "", SatelliteGroupType.ACTIVE, SatelliteGroupType.STARLINK) + @Test + fun findByGroups() { + REPOSITORY.search(groups = listOf(SatelliteGroupType.ACTIVE)).shouldHaveSize(2) + REPOSITORY.search(groups = listOf(SatelliteGroupType.STARLINK)).shouldHaveSize(1) + REPOSITORY.search(groups = listOf(SatelliteGroupType.AMATEUR)).shouldBeEmpty() + } - "find all" { - repository.search().shouldHaveSize(2) - } - "find by name" { - repository.search("iss").shouldHaveSize(1) - } - "find by groups" { - repository.search(groups = listOf(SatelliteGroupType.ACTIVE)).shouldHaveSize(2) - repository.search(groups = listOf(SatelliteGroupType.STARLINK)).shouldHaveSize(1) - repository.search(groups = listOf(SatelliteGroupType.AMATEUR)).shouldBeEmpty() - } - "find by name and groups" { - repository.search(text = "iss", groups = listOf(SatelliteGroupType.ACTIVE)).shouldHaveSize(1) - repository.search(text = "iss", groups = listOf(SatelliteGroupType.STARLINK)).shouldBeEmpty() - repository.search(text = "starlink", groups = listOf(SatelliteGroupType.EDUCATION)).shouldBeEmpty() - } + @Test + fun findByNameAndGroups() { + REPOSITORY.search(text = "iss", groups = listOf(SatelliteGroupType.ACTIVE)).shouldHaveSize(1) + REPOSITORY.search(text = "iss", groups = listOf(SatelliteGroupType.STARLINK)).shouldBeEmpty() + REPOSITORY.search(text = "starlink", groups = listOf(SatelliteGroupType.EDUCATION)).shouldBeEmpty() } companion object { @@ -51,6 +43,22 @@ class SatelliteEntityRepositoryTest : StringSpec() { 2 25544 51.6392 250.6622 0011086 22.0936 34.8107 15.49934787460571 """.trimIndent() + @JvmStatic private val BOX_STORE = MyObjectBox.builder() + .inMemory(UUID.randomUUID().toString()) + .build() + + @AfterAll + @JvmStatic + fun closeBoxStore() { + BOX_STORE.close() + } + + @JvmStatic private val BOX = BOX_STORE.boxFor() + @JvmStatic private val REPOSITORY = SatelliteRepository(BOX).apply { + save("ISS (ZARYA)", ISS_TLE, SatelliteGroupType.ACTIVE, SatelliteGroupType.EDUCATION) + save("StarLink", "", SatelliteGroupType.ACTIVE, SatelliteGroupType.STARLINK) + } + @JvmStatic internal fun SatelliteRepository.save(name: String, tle: String = "", vararg groups: SatelliteGroupType) { save(SatelliteEntity(0L, name, tle, groups.map { it.name }.toMutableList())) diff --git a/api/src/test/kotlin/SimbadDatabaseGenerator.kt b/api/src/test/kotlin/SimbadDatabaseGenerator.kt index c960c968d..2d9dfe376 100644 --- a/api/src/test/kotlin/SimbadDatabaseGenerator.kt +++ b/api/src/test/kotlin/SimbadDatabaseGenerator.kt @@ -12,6 +12,7 @@ import nebulosa.simbad.SimbadService import nebulosa.skycatalog.SkyObject import nebulosa.skycatalog.SkyObjectType import nebulosa.skycatalog.stellarium.Nebula +import nebulosa.test.concat import okhttp3.OkHttpClient import okio.sink import okio.source @@ -214,7 +215,7 @@ object SimbadDatabaseGenerator { for ((_, entity) in entities) { if (writer == null || count > 10000) { writer?.close() - writer = SimbadDatabaseWriter(Path.of("$SIMBAD_DATABASE_PATH", "simbad.%02d.dat".format(index++)).sink()) + writer = SimbadDatabaseWriter(SIMBAD_DATABASE_PATH.concat("simbad.%02d.dat".format(index++)).sink()) count = 0 } diff --git a/api/src/test/kotlin/SimbadEntityRepositoryTest.kt b/api/src/test/kotlin/SimbadEntityRepositoryTest.kt index 3ddd4487d..10df681ce 100644 --- a/api/src/test/kotlin/SimbadEntityRepositoryTest.kt +++ b/api/src/test/kotlin/SimbadEntityRepositoryTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.doubles.shouldBeExactly @@ -12,62 +11,75 @@ import nebulosa.math.deg import nebulosa.math.hours import nebulosa.nova.astrometry.Constellation import nebulosa.skycatalog.SkyObjectType +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Test import java.util.* -class SimbadEntityRepositoryTest : StringSpec() { +class SimbadEntityRepositoryTest { - init { - val boxStore = MyObjectBox.builder() - .inMemory(UUID.randomUUID().toString()) - .build() + @Test + fun findAll() { + REPOSITORY.search().shouldHaveSize(4).first().magnitude shouldBeExactly -1.45 + } - afterSpec { - boxStore.close() - } + @Test + fun findByName() { + REPOSITORY.search(name = "dolphin").shouldHaveSize(1).first().name shouldBe "Dolphin Nebula" + REPOSITORY.search(name = "andromeda").shouldBeEmpty() + REPOSITORY.search(name = "nebula").shouldHaveSize(2).first().magnitude shouldBeExactly 5.0 + } - val box = boxStore.boxFor() - val repository = SimbadEntityRepository(box) + @Test + fun findByConstellation() { + REPOSITORY.search(constellation = Constellation.CMA).shouldHaveSize(2).first().magnitude shouldBeExactly -1.45 + REPOSITORY.search(constellation = Constellation.AND).shouldBeEmpty() + } - repository.save("Sirius", SkyObjectType.STAR, Constellation.CMA, -1.45, "06 45 06".hours, "-16 43 33".deg) - repository.save("Dolphin Nebula", SkyObjectType.NEBULA, Constellation.CMA, 6.91, "06 54 11".hours, "-23 55 47".deg) - repository.save("75 Tucanae", SkyObjectType.GLOBULAR_CLUSTER, Constellation.TUC, 6.58, "01 03 12".hours, "-70 50 39".deg) - repository.save("Car Nebula", SkyObjectType.NEBULA, Constellation.CAR, 5.0, "10 45 15".hours, "-59 43 35".deg) + @Test + fun findByRegion() { + REPOSITORY.search(rightAscension = "06 45 59".hours, declination = "-20 45 29".deg, radius = 4.5.deg).shouldHaveSize(2) + .first().magnitude shouldBeExactly -1.45 + REPOSITORY.search(rightAscension = "06 45 59".hours, declination = "-20 45 29".deg, radius = 4.0.deg).shouldHaveSize(1) + .first().name shouldBe "Dolphin Nebula" + REPOSITORY.search(rightAscension = "00 42 43".hours, declination = "41 15 53".deg, radius = 10.deg).shouldBeEmpty() + } - "find all" { - repository.search().shouldHaveSize(4).first().magnitude shouldBeExactly -1.45 - } - "find by name" { - repository.search(name = "dolphin").shouldHaveSize(1).first().name shouldBe "Dolphin Nebula" - repository.search(name = "andromeda").shouldBeEmpty() - repository.search(name = "nebula").shouldHaveSize(2).first().magnitude shouldBeExactly 5.0 - } - "find by constellation" { - repository.search(constellation = Constellation.CMA).shouldHaveSize(2).first().magnitude shouldBeExactly -1.45 - repository.search(constellation = Constellation.AND).shouldBeEmpty() - } - "find by region" { - repository.search(rightAscension = "06 45 59".hours, declination = "-20 45 29".deg, radius = 4.5.deg).shouldHaveSize(2) - .first().magnitude shouldBeExactly -1.45 - repository.search(rightAscension = "06 45 59".hours, declination = "-20 45 29".deg, radius = 4.0.deg).shouldHaveSize(1) - .first().name shouldBe "Dolphin Nebula" - repository.search(rightAscension = "00 42 43".hours, declination = "41 15 53".deg, radius = 10.deg).shouldBeEmpty() - } - "find by magnitude" { - repository.search(magnitudeMin = 5.0).shouldHaveSize(3) - repository.search(magnitudeMax = 4.9).shouldHaveSize(1).first().name shouldBe "Sirius" - repository.search(magnitudeMin = 6.6, magnitudeMax = 6.99).shouldHaveSize(1).first().name shouldBe "Dolphin Nebula" - repository.search(magnitudeMax = -2.0).shouldBeEmpty() - repository.search(magnitudeMin = 7.0).shouldBeEmpty() - repository.search(magnitudeMin = 5.1, magnitudeMax = 6.0).shouldBeEmpty() - } - "find by type" { - repository.search(type = SkyObjectType.NEBULA).shouldHaveSize(2).first().magnitude shouldBeExactly 5.0 - repository.search(type = SkyObjectType.GALAXY).shouldBeEmpty() - } + @Test + fun findByMagnitude() { + REPOSITORY.search(magnitudeMin = 5.0).shouldHaveSize(3) + REPOSITORY.search(magnitudeMax = 4.9).shouldHaveSize(1).first().name shouldBe "Sirius" + REPOSITORY.search(magnitudeMin = 6.6, magnitudeMax = 6.99).shouldHaveSize(1).first().name shouldBe "Dolphin Nebula" + REPOSITORY.search(magnitudeMax = -2.0).shouldBeEmpty() + REPOSITORY.search(magnitudeMin = 7.0).shouldBeEmpty() + REPOSITORY.search(magnitudeMin = 5.1, magnitudeMax = 6.0).shouldBeEmpty() + } + + @Test + fun findByType() { + REPOSITORY.search(type = SkyObjectType.NEBULA).shouldHaveSize(2).first().magnitude shouldBeExactly 5.0 + REPOSITORY.search(type = SkyObjectType.GALAXY).shouldBeEmpty() } companion object { + @JvmStatic private val BOX_STORE = MyObjectBox.builder() + .inMemory(UUID.randomUUID().toString()) + .build() + + @AfterAll + @JvmStatic + fun closeBoxStore() { + BOX_STORE.close() + } + + @JvmStatic private val BOX = BOX_STORE.boxFor() + @JvmStatic private val REPOSITORY = SimbadEntityRepository(BOX).apply { + save("Sirius", SkyObjectType.STAR, Constellation.CMA, -1.45, "06 45 06".hours, "-16 43 33".deg) + save("Dolphin Nebula", SkyObjectType.NEBULA, Constellation.CMA, 6.91, "06 54 11".hours, "-23 55 47".deg) + save("75 Tucanae", SkyObjectType.GLOBULAR_CLUSTER, Constellation.TUC, 6.58, "01 03 12".hours, "-70 50 39".deg) + save("Car Nebula", SkyObjectType.NEBULA, Constellation.CAR, 5.0, "10 45 15".hours, "-59 43 35".deg) + } + @JvmStatic internal fun SimbadEntityRepository.save( name: String, type: SkyObjectType, constellation: Constellation, diff --git a/api/src/test/kotlin/SkyAtlasServiceTest.kt b/api/src/test/kotlin/SkyAtlasServiceTest.kt index 90ae1268f..b4a655de1 100644 --- a/api/src/test/kotlin/SkyAtlasServiceTest.kt +++ b/api/src/test/kotlin/SkyAtlasServiceTest.kt @@ -1,7 +1,6 @@ import SatelliteEntityRepositoryTest.Companion.ISS_TLE import SatelliteEntityRepositoryTest.Companion.save import SimbadEntityRepositoryTest.Companion.save -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainAll import io.kotest.matchers.collections.shouldHaveAtLeastSize import io.kotest.matchers.collections.shouldHaveSize @@ -19,141 +18,161 @@ import nebulosa.math.* import nebulosa.nova.astrometry.Constellation import nebulosa.sbd.SmallBodyDatabaseService import nebulosa.skycatalog.SkyObjectType -import nebulosa.test.AbstractFitsAndXisfTest.Companion.HTTP_CLIENT +import nebulosa.test.HTTP_CLIENT +import org.junit.jupiter.api.AfterAll +import org.junit.jupiter.api.Test import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor import java.time.LocalDateTime import java.util.* -class SkyAtlasServiceTest : StringSpec() { +class SkyAtlasServiceTest { - init { - val boxStore = MyObjectBox.builder() - .inMemory(UUID.randomUUID().toString()) - .build() + @Test + fun objectTypes() { + SERVICE.objectTypes.shouldHaveSize(2).shouldContainAll(SkyObjectType.STAR, SkyObjectType.GLOBULAR_CLUSTER) + } - afterSpec { - boxStore.close() - } + @Test + fun positionOfSun() { + val position = SERVICE.positionOfSun(LOCATION, DATE_TIME, false) + position.rightAscensionJ2000.formatHMS() shouldBe "06h38m40.2s" + position.declinationJ2000.formatSignedDMS() shouldBe "+023°08'24.6\"" + position.rightAscension.formatHMS() shouldBe "06h40m07.7s" + position.declination.formatSignedDMS() shouldBe "+023°07'10.5\"" + position.azimuth.formatDMS() shouldBe "037°55'12.8\"" + position.altitude.formatSignedDMS() shouldBe "+036°39'01.4\"" + position.magnitude shouldBeExactly -26.706 + position.constellation shouldBe Constellation.GEM + position.distance shouldBe (1.017 plusOrMinus 1e-3) + position.distanceUnit shouldBe "AU" + position.illuminated shouldBeExactly 100.0 + position.elongation.toDegrees shouldBeExactly 0.0 + } - val threadPoolTaskExecutor = ThreadPoolTaskExecutor() - val horizonsService = HorizonsService(httpClient = HTTP_CLIENT) - val horizonsEphemerisProvider = HorizonsEphemerisProvider(horizonsService) - val bodyEphemerisProvider = BodyEphemerisProvider(threadPoolTaskExecutor) - val smallBodyDatabaseService = SmallBodyDatabaseService() - val satelliteBox = boxStore.boxFor() - val satelliteRepository = SatelliteRepository(satelliteBox) - val simbadBox = boxStore.boxFor() - val simbadEntityRepository = SimbadEntityRepository(simbadBox) + @Test + fun positionOfMoon() { + val position = SERVICE.positionOfMoon(LOCATION, DATE_TIME, false) + position.rightAscensionJ2000.formatHMS() shouldBe "01h47m21.7s" + position.declinationJ2000.formatSignedDMS() shouldBe "+013°32'49.4\"" + position.rightAscension.formatHMS() shouldBe "01h48m39.8s" + position.declination.formatSignedDMS() shouldBe "+013°40'06.4\"" + position.azimuth.formatDMS() shouldBe "306°54'06.0\"" + position.altitude.formatSignedDMS() shouldBe "+037°58'29.7\"" + position.magnitude shouldBeExactly -9.302 + position.constellation shouldBe Constellation.ARI + position.distance shouldBe (367798.938 plusOrMinus 1e-3) + position.distanceUnit shouldBe "km" + position.illuminated shouldBe (32.301 plusOrMinus 1e-3) + position.elongation.toDegrees shouldBe (69.142 plusOrMinus 1e-3) + } - simbadEntityRepository.save("Sirius", SkyObjectType.STAR, Constellation.CMA, -1.45, "06 45 06".hours, "-16 43 33".deg) - simbadEntityRepository.save("75 Tucanae", SkyObjectType.GLOBULAR_CLUSTER, Constellation.TUC, 6.58, "01 03 12".hours, "-70 50 39".deg) + @Test + fun positionOfJupiter() { + val position = SERVICE.positionOfPlanet(LOCATION, "599", DATE_TIME, false) + position.rightAscensionJ2000.formatHMS() shouldBe "04h24m28.8s" + position.declinationJ2000.formatSignedDMS() shouldBe "+020°55'01.7\"" + position.rightAscension.formatHMS() shouldBe "04h25m54.0s" + position.declination.formatSignedDMS() shouldBe "+020°58'23.7\"" + position.azimuth.formatDMS() shouldBe "358°22'34.0\"" + position.altitude.formatSignedDMS() shouldBe "+049°09'53.9\"" + position.magnitude shouldBeExactly -2.025 + position.constellation shouldBe Constellation.TAU + position.distance shouldBe (5.870 plusOrMinus 1e-3) + position.distanceUnit shouldBe "AU" + position.illuminated shouldBe (99.726 plusOrMinus 1e-3) + position.elongation.toDegrees shouldBe (31.110 plusOrMinus 1e-3) + } - satelliteRepository.save("ISS (ZARYA)", ISS_TLE, SatelliteGroupType.ACTIVE, SatelliteGroupType.EDUCATION) + @Test + fun positionOfApophis() { + val apophis = SERVICE.searchMinorPlanet("apophis").spkId + val position = SERVICE.positionOfPlanet(LOCATION, "DES=$apophis;", DATE_TIME, false) + position.rightAscensionJ2000.formatHMS() shouldBe "06h33m49.8s" + position.declinationJ2000.formatSignedDMS() shouldBe "+021°37'20.6\"" + position.rightAscension.formatHMS() shouldBe "06h35m16.4s" + position.declination.formatSignedDMS() shouldBe "+021°36'16.5\"" + position.azimuth.formatDMS() shouldBe "038°00'41.8\"" + position.altitude.formatSignedDMS() shouldBe "+038°32'04.2\"" + position.magnitude shouldBeExactly 20.825 + position.constellation shouldBe Constellation.GEM + position.distance shouldBe (2.018 plusOrMinus 1e-3) + position.distanceUnit shouldBe "AU" + position.illuminated shouldBe (99.972 plusOrMinus 1e-3) + position.elongation.toDegrees shouldBe (1.885 plusOrMinus 1e-3) + } - threadPoolTaskExecutor.initialize() + @Test + fun positionOfSirius() { + val sirius = SERVICE.searchSkyObject("Sirius").shouldNotBeEmpty().first().id + val position = SERVICE.positionOfSkyObject(LOCATION, sirius, DATE_TIME) + position.rightAscensionJ2000.formatHMS() shouldBe "06h45m06.0s" + position.declinationJ2000.formatSignedDMS() shouldBe "-016°43'33.0\"" + position.rightAscension.formatHMS() shouldBe "06h46m11.5s" + position.declination.formatSignedDMS() shouldBe "-016°45'01.6\"" + position.azimuth.formatDMS() shouldBe "090°08'45.7\"" + position.altitude.formatSignedDMS() shouldBe "+057°41'06.5\"" + position.magnitude shouldBeExactly -1.45 + position.constellation shouldBe Constellation.CMA + } - val service = SkyAtlasService( - horizonsEphemerisProvider, bodyEphemerisProvider, smallBodyDatabaseService, - satelliteRepository, simbadEntityRepository, HTTP_CLIENT - ) + @Test + fun positionOfIss() { + val iss = SERVICE.searchSatellites("ISS", emptyList()).shouldNotBeEmpty().first() + val position = SERVICE.positionOfSatellite(LOCATION, iss, DATE_TIME) + position.rightAscensionJ2000.formatHMS() shouldBe "14h47m37.8s" + position.declinationJ2000.formatSignedDMS() shouldBe "-017°22'47.2\"" + position.rightAscension.formatHMS() shouldBe "14h49m00.4s" + position.declination.formatSignedDMS() shouldBe "-017°29'00.1\"" + position.azimuth.formatDMS() shouldBe "144°36'58.9\"" + position.altitude.formatSignedDMS() shouldBe "-045°07'44.9\"" + position.constellation shouldBe Constellation.LIB + position.distance shouldBe (9633.950 plusOrMinus 1e-3) + position.distanceUnit shouldBe "km" + position.illuminated shouldBe (79.282 plusOrMinus 1e-3) + position.elongation.toDegrees shouldBe (125.849 plusOrMinus 1e-3) + } - val location = Location("-19.846616".deg, "-43.96872".deg, 852.0.m, -180) - val dateTime = LocalDateTime.of(2024, 6, 30, 9, 50, 0) + @Test + fun closeApproaches() { + val phas = SERVICE.closeApproachesForMinorPlanets(7, 10.0, DATE_TIME.toLocalDate()) + phas.shouldHaveAtLeastSize(2).map { it.name }.shouldContainAll("(2017 MB3)", "(2024 LH)") + } - "object types" { - service.objectTypes.shouldHaveSize(2).shouldContainAll(SkyObjectType.STAR, SkyObjectType.GLOBULAR_CLUSTER) - } - "position of sun" { - val position = service.positionOfSun(location, dateTime, false) - position.rightAscensionJ2000.formatHMS() shouldBe "06h38m40.2s" - position.declinationJ2000.formatSignedDMS() shouldBe "+023°08'24.6\"" - position.rightAscension.formatHMS() shouldBe "06h40m07.7s" - position.declination.formatSignedDMS() shouldBe "+023°07'10.5\"" - position.azimuth.formatDMS() shouldBe "037°55'12.8\"" - position.altitude.formatSignedDMS() shouldBe "+036°39'01.4\"" - position.magnitude shouldBeExactly -26.706 - position.constellation shouldBe Constellation.GEM - position.distance shouldBe (1.017 plusOrMinus 1e-3) - position.distanceUnit shouldBe "AU" - position.illuminated shouldBeExactly 100.0 - position.elongation.toDegrees shouldBeExactly 0.0 - } - "position of moon" { - val position = service.positionOfMoon(location, dateTime, false) - position.rightAscensionJ2000.formatHMS() shouldBe "01h47m21.7s" - position.declinationJ2000.formatSignedDMS() shouldBe "+013°32'49.4\"" - position.rightAscension.formatHMS() shouldBe "01h48m39.8s" - position.declination.formatSignedDMS() shouldBe "+013°40'06.4\"" - position.azimuth.formatDMS() shouldBe "306°54'06.0\"" - position.altitude.formatSignedDMS() shouldBe "+037°58'29.7\"" - position.magnitude shouldBeExactly -9.302 - position.constellation shouldBe Constellation.ARI - position.distance shouldBe (367798.938 plusOrMinus 1e-3) - position.distanceUnit shouldBe "km" - position.illuminated shouldBe (32.301 plusOrMinus 1e-3) - position.elongation.toDegrees shouldBe (69.142 plusOrMinus 1e-3) - } - "position of jupiter" { - val position = service.positionOfPlanet(location, "599", dateTime, false) - position.rightAscensionJ2000.formatHMS() shouldBe "04h24m28.8s" - position.declinationJ2000.formatSignedDMS() shouldBe "+020°55'01.7\"" - position.rightAscension.formatHMS() shouldBe "04h25m54.0s" - position.declination.formatSignedDMS() shouldBe "+020°58'23.7\"" - position.azimuth.formatDMS() shouldBe "358°22'34.0\"" - position.altitude.formatSignedDMS() shouldBe "+049°09'53.9\"" - position.magnitude shouldBeExactly -2.025 - position.constellation shouldBe Constellation.TAU - position.distance shouldBe (5.870 plusOrMinus 1e-3) - position.distanceUnit shouldBe "AU" - position.illuminated shouldBe (99.726 plusOrMinus 1e-3) - position.elongation.toDegrees shouldBe (31.110 plusOrMinus 1e-3) - } - "position of apophis" { - val apophis = service.searchMinorPlanet("apophis").spkId - val position = service.positionOfPlanet(location, "DES=$apophis;", dateTime, false) - position.rightAscensionJ2000.formatHMS() shouldBe "06h33m49.8s" - position.declinationJ2000.formatSignedDMS() shouldBe "+021°37'20.6\"" - position.rightAscension.formatHMS() shouldBe "06h35m16.4s" - position.declination.formatSignedDMS() shouldBe "+021°36'16.5\"" - position.azimuth.formatDMS() shouldBe "038°00'41.8\"" - position.altitude.formatSignedDMS() shouldBe "+038°32'04.2\"" - position.magnitude shouldBeExactly 20.825 - position.constellation shouldBe Constellation.GEM - position.distance shouldBe (2.018 plusOrMinus 1e-3) - position.distanceUnit shouldBe "AU" - position.illuminated shouldBe (99.972 plusOrMinus 1e-3) - position.elongation.toDegrees shouldBe (1.885 plusOrMinus 1e-3) - } - "position of sirius" { - val sirius = service.searchSkyObject("Sirius").shouldNotBeEmpty().first().id - val position = service.positionOfSkyObject(location, sirius, dateTime) - position.rightAscensionJ2000.formatHMS() shouldBe "06h45m06.0s" - position.declinationJ2000.formatSignedDMS() shouldBe "-016°43'33.0\"" - position.rightAscension.formatHMS() shouldBe "06h46m11.5s" - position.declination.formatSignedDMS() shouldBe "-016°45'01.6\"" - position.azimuth.formatDMS() shouldBe "090°08'45.7\"" - position.altitude.formatSignedDMS() shouldBe "+057°41'06.5\"" - position.magnitude shouldBeExactly -1.45 - position.constellation shouldBe Constellation.CMA + companion object { + + @JvmStatic private val BOX_STORE = MyObjectBox.builder() + .inMemory(UUID.randomUUID().toString()) + .build() + + @AfterAll + @JvmStatic + fun closeBoxStore() { + BOX_STORE.close() } - "position of iss" { - val iss = service.searchSatellites("ISS", emptyList()).shouldNotBeEmpty().first() - val position = service.positionOfSatellite(location, iss, dateTime) - position.rightAscensionJ2000.formatHMS() shouldBe "14h47m37.8s" - position.declinationJ2000.formatSignedDMS() shouldBe "-017°22'47.2\"" - position.rightAscension.formatHMS() shouldBe "14h49m00.4s" - position.declination.formatSignedDMS() shouldBe "-017°29'00.1\"" - position.azimuth.formatDMS() shouldBe "144°36'58.9\"" - position.altitude.formatSignedDMS() shouldBe "-045°07'44.9\"" - position.constellation shouldBe Constellation.LIB - position.distance shouldBe (9633.950 plusOrMinus 1e-3) - position.distanceUnit shouldBe "km" - position.illuminated shouldBe (79.282 plusOrMinus 1e-3) - position.elongation.toDegrees shouldBe (125.849 plusOrMinus 1e-3) + + @JvmStatic private val THREAD_POOL_TASK_EXECUTOR = ThreadPoolTaskExecutor().also { it.initialize() } + @JvmStatic private val HORIZONS_SERVICE = HorizonsService(httpClient = HTTP_CLIENT) + @JvmStatic private val HORIZONS_EPHEMERIS_PROVIDER = HorizonsEphemerisProvider(HORIZONS_SERVICE) + @JvmStatic private val BODY_EPHEMERIS_PROVIDER = BodyEphemerisProvider(THREAD_POOL_TASK_EXECUTOR) + @JvmStatic private val SMALL_BODY_DATABASE_SERVICE = SmallBodyDatabaseService() + @JvmStatic private val SATELLITE_BOX = BOX_STORE.boxFor() + @JvmStatic private val SIMBAD_BOX = BOX_STORE.boxFor() + + @JvmStatic private val SIMBAD_ENTITY_REPOSITORY = SimbadEntityRepository(SIMBAD_BOX).apply { + save("Sirius", SkyObjectType.STAR, Constellation.CMA, -1.45, "06 45 06".hours, "-16 43 33".deg) + save("75 Tucanae", SkyObjectType.GLOBULAR_CLUSTER, Constellation.TUC, 6.58, "01 03 12".hours, "-70 50 39".deg) } - "close approaches" { - val phas = service.closeApproachesForMinorPlanets(7, 10.0, dateTime.toLocalDate()) - phas.shouldHaveAtLeastSize(2).map { it.name }.shouldContainAll("(2017 MB3)", "(2024 LH)") + + @JvmStatic private val SATELLITE_REPOSITORY = SatelliteRepository(SATELLITE_BOX).apply { + save("ISS (ZARYA)", ISS_TLE, SatelliteGroupType.ACTIVE, SatelliteGroupType.EDUCATION) } + + @JvmStatic private val SERVICE = SkyAtlasService( + HORIZONS_EPHEMERIS_PROVIDER, BODY_EPHEMERIS_PROVIDER, SMALL_BODY_DATABASE_SERVICE, + SATELLITE_REPOSITORY, SIMBAD_ENTITY_REPOSITORY, HTTP_CLIENT + ) + + @JvmStatic private val LOCATION = Location("-19.846616".deg, "-43.96872".deg, 852.0.m, -180) + @JvmStatic private val DATE_TIME = LocalDateTime.of(2024, 6, 30, 9, 50, 0) } } diff --git a/api/src/test/kotlin/StackerServiceTest.kt b/api/src/test/kotlin/StackerServiceTest.kt index 77a0f664d..e27fa0a0a 100644 --- a/api/src/test/kotlin/StackerServiceTest.kt +++ b/api/src/test/kotlin/StackerServiceTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.annotation.EnabledIf import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe @@ -6,18 +5,44 @@ import nebulosa.api.stacker.* import nebulosa.fits.fits import nebulosa.image.Image import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction -import nebulosa.test.AbstractFitsAndXisfTest -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.NonGitHubOnly +import nebulosa.test.save +import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.io.path.createDirectories -@EnabledIf(NonGitHubOnlyCondition::class) -class StackerServiceTest : AbstractFitsAndXisfTest() { +@NonGitHubOnly +class StackerServiceTest { - init { - val service = StackerService() + @Test + fun stackLRGB() { + val targets = PATHS.map { + val analyzed = SERVICE.analyze(it)!! + StackingTarget(true, it, analyzed.group, true) + } + + targets.count { it.group == StackerGroupType.LUMINANCE } shouldBeExactly 10 + targets.count { it.group == StackerGroupType.RED } shouldBeExactly 3 + targets.count { it.group == StackerGroupType.GREEN } shouldBeExactly 3 + targets.count { it.group == StackerGroupType.BLUE } shouldBeExactly 3 + + val request = StackingRequest( + Path.of(BASE_DIR, "stacker").createDirectories(), StackerType.PIXINSIGHT, + Path.of("PixInsight"), DARK_PATH, true, null, false, null, false, false, + 1, PATHS[0], targets + ) + + val image = SERVICE.stack(request).shouldNotBeNull().fits().use(Image::open) + image.transform(AutoScreenTransformFunction).save("stacker-lrgb").second shouldBe "465a296bb4582ab2f938757347500eb8" + } + + companion object { + + const val BASE_DIR = "/home/tiagohm/Imagens/Astrophotos/Light/Algieba/2024-05-13" + + @JvmStatic private val SERVICE = StackerService() - val paths = listOf( + @JvmStatic private val PATHS = listOf( Path.of("$BASE_DIR/20240513.213424625-LIGHT.fits"), Path.of("$BASE_DIR/20240513.213436506-LIGHT.fits"), Path.of("$BASE_DIR/20240513.213448253-LIGHT.fits"), @@ -39,32 +64,6 @@ class StackerServiceTest : AbstractFitsAndXisfTest() { Path.of("$BASE_DIR/20240513.213802188-LIGHT.fits"), ) - val darkPath = Path.of("/home/tiagohm/Imagens/Astrophotos/Dark/2024-06-08/ASI294_BIN4_G120_O80/10-DARK.fits") - - "stack LRGB" { - val targets = paths.map { - val analyzed = service.analyze(it)!! - StackingTarget(true, it, analyzed.group, true) - } - - targets.count { it.group == StackerGroupType.LUMINANCE } shouldBeExactly 10 - targets.count { it.group == StackerGroupType.RED } shouldBeExactly 3 - targets.count { it.group == StackerGroupType.GREEN } shouldBeExactly 3 - targets.count { it.group == StackerGroupType.BLUE } shouldBeExactly 3 - - val request = StackingRequest( - Path.of(BASE_DIR, "stacker").createDirectories(), StackerType.PIXINSIGHT, - Path.of("PixInsight"), darkPath, true, null, false, null, false, false, - 1, paths[0], targets - ) - - val image = service.stack(request).shouldNotBeNull().fits().use(Image::open) - image.transform(AutoScreenTransformFunction).save("stacker-lrgb").second shouldBe "465a296bb4582ab2f938757347500eb8" - } - } - - companion object { - - const val BASE_DIR = "/home/tiagohm/Imagens/Astrophotos/Light/Algieba/2024-05-13" + @JvmStatic private val DARK_PATH = Path.of("/home/tiagohm/Imagens/Astrophotos/Dark/2024-06-08/ASI294_BIN4_G120_O80/10-DARK.fits") } } diff --git a/build.gradle.kts b/build.gradle.kts index 62d8f21ab..f87cbc51c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,3 @@ -import com.adarshr.gradle.testlogger.TestLoggerExtension -import com.adarshr.gradle.testlogger.theme.ThemeType import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -8,7 +6,6 @@ buildscript { dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0") classpath("org.jetbrains.kotlin:kotlin-allopen:2.0.0") - classpath("com.adarshr:gradle-test-logger-plugin:4.0.0") classpath("io.objectbox:objectbox-gradle-plugin:4.0.1") } @@ -44,22 +41,6 @@ subprojects { val project = this@subprojects if (project.name == "nebulosa-bom") return@subprojects - apply { - plugin("com.adarshr.test-logger") - } - - configure { - theme = ThemeType.STANDARD - slowThreshold = 2000L - showStackTraces = false - showSkipped = true - showStandardStreams = true - showPassedStandardStreams = true - showSkippedStandardStreams = false - showFailedStandardStreams = true - logLevel = LogLevel.QUIET - } - tasks.withType { compilerOptions { jvmTarget.set(JvmTarget.JVM_17) @@ -84,10 +65,11 @@ subprojects { exceptionFormat = TestExceptionFormat.FULL } - systemProperty("junit.jupiter.extensions.autodetection.enabled", "true") - systemProperty("kotest.framework.classpath.scanning.config.disable", "true") - systemProperty("kotest.framework.classpath.scanning.autoscan.disable", "true") + systemProperty("junit.jupiter.extensions.autodetection.enabled", "false") systemProperty("github", System.getProperty("github", "false")) + systemProperty("root.dir", "$rootDir") + systemProperty("project.dir", "$projectDir") + systemProperty("app.dir", "$rootDir/data") // LOGBACK } tasks.withType { diff --git a/nebulosa-adql/src/test/kotlin/QueryBuilderTest.kt b/nebulosa-adql/src/test/kotlin/QueryBuilderTest.kt index d6fb6b864..b9e508e21 100644 --- a/nebulosa-adql/src/test/kotlin/QueryBuilderTest.kt +++ b/nebulosa-adql/src/test/kotlin/QueryBuilderTest.kt @@ -1,639 +1,745 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import nebulosa.adql.* import nebulosa.math.deg +import org.junit.jupiter.api.Test -class QueryBuilderTest : StringSpec() { +class QueryBuilderTest { - init { - val oid = Column("b.oid") - val rightAscension = Column("b.ra") - val declination = Column("b.dec") - val magnitude = Column("f.V") - val name = Column("ident.id") - - "basic" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - val query = builder.build().toString() - query shouldBe """ + @Test + fun basic() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic """.trimIndent() - } - "limit" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(Limit(100)) - builder.add(From("basic")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun limit() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(Limit(100)) + builder.add(From("basic")) + val query = builder.build().toString() + query shouldBe """ SELECT TOP 100 b.oid FROM basic """.trimIndent() - } - "multiple columns" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(rightAscension) - builder.add(From("basic")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun multipleColumns() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(RA) + builder.add(From("basic")) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid , b.ra FROM basic """.trimIndent() - } - "column alias" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(Column("b.plx_value as plx")) - builder.add(From("basic")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun columnAlias() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(Column("b.plx_value as plx")) + builder.add(From("basic")) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid , b.plx_value as plx FROM basic """.trimIndent() - } - "table alias" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic").alias("b")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun tableAlias() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic").alias("b")) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b """.trimIndent() - } - "distinct" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(Distinct) - builder.add(From("basic")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun distinct() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(Distinct) + builder.add(From("basic")) + val query = builder.build().toString() + query shouldBe """ SELECT DISTINCT b.oid FROM basic """.trimIndent() - } - "is not null" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(rightAscension.isNotNull) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun isNotNull() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(RA.isNotNull) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.ra IS NOT NULL """.trimIndent() - } - "is null" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(rightAscension.isNull) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun isNull() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(RA.isNull) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.ra IS NULL """.trimIndent() - } - "equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(oid equal 8) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun equal() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(OID equal 8) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.oid = 8 """.trimIndent() - } - "not equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(oid notEqual 8) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun notEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(OID notEqual 8) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.oid != 8 """.trimIndent() - } - "between" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(magnitude between -8.0..4.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun between() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(MAG between -8.0..4.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V BETWEEN -8.0 AND 4.0 """.trimIndent() - } - "not between" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(magnitude notBetween -8.0..4.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun notBetween() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(MAG notBetween -8.0..4.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V NOT BETWEEN -8.0 AND 4.0 """.trimIndent() - } - "less than" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(magnitude lessThan 4.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun lessThan() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(MAG lessThan 4.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V < 4.0 """.trimIndent() - } - "less or equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(magnitude lessOrEqual 4.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun lessOrEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(MAG lessOrEqual 4.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V <= 4.0 """.trimIndent() - } - "greater than" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(magnitude greaterThan 4.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun greaterThan() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(MAG greaterThan 4.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V > 4.0 """.trimIndent() - } - "greater or equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(magnitude greaterOrEqual 4.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun greaterOrEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(MAG greaterOrEqual 4.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V >= 4.0 """.trimIndent() - } - "like" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(name like "NGC%") - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun like() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(NAME like "NGC%") + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE ident.id LIKE 'NGC%' """.trimIndent() - } - "not like" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(name notLike "NGC%") - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun notLike() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(NAME notLike "NGC%") + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE ident.id NOT LIKE 'NGC%' """.trimIndent() - } - "negated is not null" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!rightAscension.isNotNull) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedIsNotNull() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!RA.isNotNull) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.ra IS NULL """.trimIndent() - } - "negated is null" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!rightAscension.isNull) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedIsNull() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!RA.isNull) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.ra IS NOT NULL """.trimIndent() - } - "negated equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(oid equal 8)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(OID equal 8)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.oid != 8 """.trimIndent() - } - "negated not equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(oid notEqual 8)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedNotEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(OID notEqual 8)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.oid = 8 """.trimIndent() - } - "negated between" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude between -8.0..4.0)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedBetween() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG between -8.0..4.0)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V NOT BETWEEN -8.0 AND 4.0 """.trimIndent() - } - "negated not between" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude notBetween -8.0..4.0)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedNotBetween() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG notBetween -8.0..4.0)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V BETWEEN -8.0 AND 4.0 """.trimIndent() - } - "negated less than" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude lessThan 4.0)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedLessThan() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG lessThan 4.0)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V >= 4.0 """.trimIndent() - } - "negated less or equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude lessOrEqual 4.0)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedLessOrEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG lessOrEqual 4.0)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V > 4.0 """.trimIndent() - } - "negated greater than" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude greaterThan 4.0)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedGreaterThan() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG greaterThan 4.0)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V <= 4.0 """.trimIndent() - } - "negated greater or equal" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude greaterOrEqual 4.0)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedGreaterOrEqual() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG greaterOrEqual 4.0)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V < 4.0 """.trimIndent() - } - "negated not like" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(name notLike "NGC%")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedNotLike() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(NAME notLike "NGC%")) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE ident.id LIKE 'NGC%' """.trimIndent() - } - "negated like" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(name like "NGC%")) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedLike() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(NAME like "NGC%")) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE ident.id NOT LIKE 'NGC%' """.trimIndent() - } - "and" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(oid equal 8) - builder.add(magnitude lessOrEqual 8) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun and() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(OID equal 8) + builder.add(MAG lessOrEqual 8) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE b.oid = 8 AND f.V <= 8 """.trimIndent() - } - "or" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(oid equal 8 or (oid equal 9)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun or() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(OID equal 8 or (OID equal 9)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE (b.oid = 8 OR b.oid = 9) """.trimIndent() - } - "and & or" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(oid equal 8 or (oid equal 9 and (magnitude lessOrEqual 8.5))) - var query = builder.build().toString() - query shouldBe """ + } + + @Test + fun andAndOr() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(OID equal 8 or (OID equal 9 and (MAG lessOrEqual 8.5))) + var query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE (b.oid = 8 OR (b.oid = 9 AND f.V <= 8.5)) """.trimIndent() - builder.clear() - builder.add(oid) - builder.add(From("basic")) - builder.add(oid equal 8 or (oid equal 9) and (magnitude lessOrEqual 8.5)) - query = builder.build().toString() - query shouldBe """ + builder.clear() + builder.add(OID) + builder.add(From("basic")) + builder.add(OID equal 8 or (OID equal 9) and (MAG lessOrEqual 8.5)) + query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE ((b.oid = 8 OR b.oid = 9) AND f.V <= 8.5) """.trimIndent() - } - "negated and" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(oid equal 8 and (magnitude lessOrEqual 8))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedAnd() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(OID equal 8 and (MAG lessOrEqual 8))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE NOT (b.oid = 8 AND f.V <= 8) """.trimIndent() - } - "negated or" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(oid equal 8 or (oid equal 9))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedOr() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(OID equal 8 or (OID equal 9))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE NOT (b.oid = 8 OR b.oid = 9) """.trimIndent() - } - "negative" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude greaterOrEqual -(4.0.operand))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negative() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG greaterOrEqual -(4.0.operand))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V < -4.0 """.trimIndent() - } - "double negative should be positive" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(!(magnitude greaterOrEqual -(-(4.0.operand)))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun doubleNegativeShouldBePositive() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(!(MAG greaterOrEqual -(-(4.0.operand)))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic WHERE f.V < 4.0 """.trimIndent() - } - "sort by" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic")) - builder.add(SortBy(oid, SortDirection.DESCENDING)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun sortBy() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic")) + builder.add(SortBy(OID, SortDirection.DESCENDING)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic ORDER BY oid DESC """.trimIndent() - } - "left join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(LeftJoin(From("basic"), From("ident"), arrayOf(oid equal Column("ident.oidref")))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun leftJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(LeftJoin(From("basic"), From("ident"), arrayOf(OID equal Column("ident.oidref")))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic LEFT OUTER JOIN ident ON b.oid = ident.oidref """.trimIndent() - } - "right join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(RightJoin(From("basic"), From("ident"), arrayOf(oid equal Column("ident.oidref")))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun rightJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(RightJoin(From("basic"), From("ident"), arrayOf(OID equal Column("ident.oidref")))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic RIGHT OUTER JOIN ident ON b.oid = ident.oidref """.trimIndent() - } - "full join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(FullJoin(From("basic"), From("ident"), arrayOf(oid equal Column("ident.oidref")))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun fullJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(FullJoin(From("basic"), From("ident"), arrayOf(OID equal Column("ident.oidref")))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic FULL OUTER JOIN ident ON b.oid = ident.oidref """.trimIndent() - } - "inner join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(InnerJoin(From("basic"), From("ident"), arrayOf(oid equal Column("ident.oidref")))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun innerJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(InnerJoin(From("basic"), From("ident"), arrayOf(OID equal Column("ident.oidref")))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic INNER JOIN ident ON b.oid = ident.oidref """.trimIndent() - } - "natural left join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(LeftJoin(From("basic"), From("ident"))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun naturalLeftJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(LeftJoin(From("basic"), From("ident"))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic NATURAL LEFT OUTER JOIN ident """.trimIndent() - } - "natural right join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(RightJoin(From("basic"), From("ident"))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun naturalRightJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(RightJoin(From("basic"), From("ident"))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic NATURAL RIGHT OUTER JOIN ident """.trimIndent() - } - "natural full join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(FullJoin(From("basic"), From("ident"))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun naturalFullJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(FullJoin(From("basic"), From("ident"))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic NATURAL FULL OUTER JOIN ident """.trimIndent() - } - "natural inner join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(InnerJoin(From("basic"), From("ident"))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun naturalInnerJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(InnerJoin(From("basic"), From("ident"))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic NATURAL INNER JOIN ident """.trimIndent() - } - "cross join" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(CrossJoin(From("basic"), From("ident"))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun crossJoin() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(CrossJoin(From("basic"), From("ident"))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic CROSS JOIN ident """.trimIndent() - } - "multiple join" { - val builder = QueryBuilder() - builder.add(oid) - var join: Table = LeftJoin(From("basic").alias("b"), From("ident"), arrayOf(oid equal Column("ident.oidref"))) - join = LeftJoin(join, From("allfluxes").alias("f"), arrayOf(oid equal Column("f.oidref"))) - join = LeftJoin(join, From("ids"), arrayOf(oid equal Column("ids.oidref"))) - builder.add(join) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun multipleJoin() { + val builder = QueryBuilder() + builder.add(OID) + var join: Table = LeftJoin(From("basic").alias("b"), From("ident"), arrayOf(OID equal Column("ident.oidref"))) + join = LeftJoin(join, From("allfluxes").alias("f"), arrayOf(OID equal Column("f.oidref"))) + join = LeftJoin(join, From("ids"), arrayOf(OID equal Column("ids.oidref"))) + builder.add(join) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b LEFT OUTER JOIN ident ON b.oid = ident.oidref LEFT OUTER JOIN allfluxes AS f ON b.oid = f.oidref LEFT OUTER JOIN ids ON b.oid = ids.oidref """.trimIndent() - } - "contains" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic").alias("b")) - builder.add(SkyPoint(rightAscension, declination) contains Circle(250.42.deg, 36.46.deg, 0.1.deg)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun contains() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic").alias("b")) + builder.add(SkyPoint(RA, DEC) contains Circle(250.42.deg, 36.46.deg, 0.1.deg)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b WHERE CONTAINS(POINT('ICRS', b.ra, b.dec), CIRCLE('ICRS', 250.42, 36.46, 0.1)) = 1 """.trimIndent() - } - "not contains" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic").alias("b")) - builder.add(SkyPoint(rightAscension, declination) notContains Box(250.42.deg, 36.46.deg, 0.1.deg, 0.2.deg)) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun notContains() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic").alias("b")) + builder.add(SkyPoint(RA, DEC) notContains Box(250.42.deg, 36.46.deg, 0.1.deg, 0.2.deg)) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b WHERE CONTAINS(POINT('ICRS', b.ra, b.dec), BOX('ICRS', 250.42, 36.46, 0.1, 0.2)) = 0 """.trimIndent() - } - "negated contains" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic").alias("b")) - builder.add(!(SkyPoint(rightAscension, declination) contains Circle(250.42.deg, 36.46.deg, 0.1.deg))) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun negatedContains() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic").alias("b")) + builder.add(!(SkyPoint(RA, DEC) contains Circle(250.42.deg, 36.46.deg, 0.1.deg))) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b WHERE CONTAINS(POINT('ICRS', b.ra, b.dec), CIRCLE('ICRS', 250.42, 36.46, 0.1)) = 0 """.trimIndent() - } - "distance" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic").alias("b")) - builder.add(SkyPoint(rightAscension, declination) distance SkyPoint(250.42.deg, 36.46.deg) lessOrEqual 8.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun distance() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic").alias("b")) + builder.add(SkyPoint(RA, DEC) distance SkyPoint(250.42.deg, 36.46.deg) lessOrEqual 8.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b WHERE DISTANCE(POINT('ICRS', b.ra, b.dec), POINT('ICRS', 250.42, 36.46)) <= 8.0 """.trimIndent() - } - "area" { - val builder = QueryBuilder() - builder.add(oid) - builder.add(From("basic").alias("b")) - builder.add(Area(Box(rightAscension, declination, 0.1.deg, 0.2.deg)) lessOrEqual 8.0) - val query = builder.build().toString() - query shouldBe """ + } + + @Test + fun area() { + val builder = QueryBuilder() + builder.add(OID) + builder.add(From("basic").alias("b")) + builder.add(Area(Box(RA, DEC, 0.1.deg, 0.2.deg)) lessOrEqual 8.0) + val query = builder.build().toString() + query shouldBe """ SELECT b.oid FROM basic AS b WHERE AREA(BOX('ICRS', b.ra, b.dec, 0.1, 0.2)) <= 8.0 """.trimIndent() - } + } + + companion object { + + @JvmStatic private val OID = Column("b.oid") + @JvmStatic private val RA = Column("b.ra") + @JvmStatic private val DEC = Column("b.dec") + @JvmStatic private val MAG = Column("f.V") + @JvmStatic private val NAME = Column("ident.id") } } diff --git a/nebulosa-alignment/src/test/kotlin/ThreePointPolarAlignmentTest.kt b/nebulosa-alignment/src/test/kotlin/ThreePointPolarAlignmentTest.kt index 5808474b9..deed0bab5 100644 --- a/nebulosa-alignment/src/test/kotlin/ThreePointPolarAlignmentTest.kt +++ b/nebulosa-alignment/src/test/kotlin/ThreePointPolarAlignmentTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe @@ -12,74 +11,84 @@ import nebulosa.math.toArcsec import nebulosa.platesolver.PlateSolution import nebulosa.time.TimeYMDHMS import nebulosa.time.UTC +import org.junit.jupiter.api.Test -class ThreePointPolarAlignmentTest : StringSpec() { +class ThreePointPolarAlignmentTest { - init { - // Based on logs generated by N.I.N.A. using Telescope Simulator for .NET and Sky Simulator (ASCOM). - // https://sourceforge.net/projects/sky-simulator/ + // Based on logs generated by N.I.N.A. using Telescope Simulator for .NET and Sky Simulator (ASCOM). + // https://sourceforge.net/projects/sky-simulator/ - "position" { - val a = Position("04:14:08".hours, "-05 26 10".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 59, 13.3739))) - val b = Position(a.vector, LNG, SLAT) + @Test + fun position() { + val a = Position("04:14:08".hours, "-05 26 10".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 59, 13.3739))) + val b = Position(a.vector, LNG, SLAT) - a.topocentric.azimuth shouldBeExactly b.topocentric.azimuth - a.topocentric.altitude shouldBeExactly b.topocentric.altitude - } - "stenographic projection" { - val coordinates = doubleArrayOf(5.0.arcsec, 8.0.arcsec) - val projected = coordinates.stenographicProjection(0.0, 0.0, 100.0, 100.0, 1.0.arcsec, 0.0) - projected[0] shouldBe (95.0 plusOrMinus 1e-8) - projected[1] shouldBe (92.0 plusOrMinus 1e-8) - } - "destination coordinates" { - val position1 = Position("05:35:18".hours, "-05 23 26".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 42.4979))) - val position2 = Position("04:54:45".hours, "-05 24 50".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 58.1655))) - val position3 = Position("04:14:08".hours, "-05 26 10".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 59, 13.3739))) - val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:14:08".hours, "-05 26 10".deg, 1280.0, 1024.0) - val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, SLAT) + a.topocentric.azimuth shouldBeExactly b.topocentric.azimuth + a.topocentric.altitude shouldBeExactly b.topocentric.altitude + } - with(pe.destinationCoordinates(0.0, 0.0)) { - this[0] shouldBe ("04:14:08".hours plusOrMinus 1e-14) - this[1] shouldBe ("-05 26 10".deg plusOrMinus 1e-14) - } - } - "perfectly aligned" { - val position1 = Position("05:35:18".hours, "-05 23 26".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 42.4979))) - val position2 = Position("04:54:45".hours, "-05 24 50".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 58.1655))) - val position3 = Position("04:14:08".hours, "-05 26 10".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 59, 13.3739))) - val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:14:08".hours, "-05 26 10".deg, 1280.0, 1024.0) - val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, SLAT) - val (az, alt) = pe.compute() + @Test + fun stenographicProjection() { + val coordinates = doubleArrayOf(5.0.arcsec, 8.0.arcsec) + val projected = coordinates.stenographicProjection(0.0, 0.0, 100.0, 100.0, 1.0.arcsec, 0.0) + projected[0] shouldBe (95.0 plusOrMinus 1e-8) + projected[1] shouldBe (92.0 plusOrMinus 1e-8) + } - // Calculated Error: Az: -00° 00' 04", Alt: -00° 00' 07", Tot: 00° 00' 08" - az.toArcsec shouldBe (-4.0 plusOrMinus 2.5) - alt.toArcsec shouldBe (-7.0 plusOrMinus 2.5) - } - "bad southern polar aligned" { - val position1 = Position("05:35:29".hours, "-05 23 44".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 2, 28.0693))) - val position2 = Position("04:54:48".hours, "-05 23 16".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 2, 43.0120))) - val position3 = Position("04:14:05".hours, "-05 22 47".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 2, 57.8800))) - val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:14:05".hours, "-05 22 47".deg, 1280.0, 1024.0) - val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, SLAT) - val (az, alt) = pe.compute() + @Test + fun destinationCoordinates() { + val position1 = Position("05:35:18".hours, "-05 23 26".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 42.4979))) + val position2 = Position("04:54:45".hours, "-05 24 50".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 58.1655))) + val position3 = Position("04:14:08".hours, "-05 26 10".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 59, 13.3739))) + val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:14:08".hours, "-05 26 10".deg, 1280.0, 1024.0) + val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, SLAT) - // Calculated Error: Az: 00° 10' 10", Alt: 00° 04' 41", Tot: 00° 11' 11" - az.toArcsec shouldBe (610.0 plusOrMinus 7.0) - alt.toArcsec shouldBe (281.0 plusOrMinus 7.0) + with(pe.destinationCoordinates(0.0, 0.0)) { + this[0] shouldBe ("04:14:08".hours plusOrMinus 1e-14) + this[1] shouldBe ("-05 26 10".deg plusOrMinus 1e-14) } - "bad northern polar aligned" { - val position1 = Position("05:35:35".hours, "-05 32 31".deg, LNG, NLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 19, 31.1390))) - val position2 = Position("04:54:49".hours, "-05 34 43".deg, LNG, NLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 19, 46.2383))) - val position3 = Position("04:13:55".hours, "-05 36 32".deg, LNG, NLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 20, 1.6394))) - val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:13:55".hours, "-05 36 32".deg, 1280.0, 1024.0) - val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, NLAT) - val (az, alt) = pe.compute() + } - // Calculated Error: Az: -00° 09' 58", Alt: 00° 04' 51", Tot: 00° 11' 05" - az.toArcsec shouldBe (-598.0 plusOrMinus 7.0) - alt.toArcsec shouldBe (291.0 plusOrMinus 7.0) - } + @Test + fun perfectlyAligned() { + val position1 = Position("05:35:18".hours, "-05 23 26".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 42.4979))) + val position2 = Position("04:54:45".hours, "-05 24 50".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 58, 58.1655))) + val position3 = Position("04:14:08".hours, "-05 26 10".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 10, 22, 59, 13.3739))) + val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:14:08".hours, "-05 26 10".deg, 1280.0, 1024.0) + val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, SLAT) + val (az, alt) = pe.compute() + + // Calculated Error: Az: -00° 00' 04", Alt: -00° 00' 07", Tot: 00° 00' 08" + az.toArcsec shouldBe (-4.0 plusOrMinus 2.5) + alt.toArcsec shouldBe (-7.0 plusOrMinus 2.5) + } + + @Test + fun badSouthernPolarAligned() { + val position1 = Position("05:35:29".hours, "-05 23 44".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 2, 28.0693))) + val position2 = Position("04:54:48".hours, "-05 23 16".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 2, 43.0120))) + val position3 = Position("04:14:05".hours, "-05 22 47".deg, LNG, SLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 2, 57.8800))) + val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:14:05".hours, "-05 22 47".deg, 1280.0, 1024.0) + val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, SLAT) + val (az, alt) = pe.compute() + + // Calculated Error: Az: 00° 10' 10", Alt: 00° 04' 41", Tot: 00° 11' 11" + az.toArcsec shouldBe (610.0 plusOrMinus 7.0) + alt.toArcsec shouldBe (281.0 plusOrMinus 7.0) + } + + @Test + fun badNorthernPolarAligned() { + val position1 = Position("05:35:35".hours, "-05 32 31".deg, LNG, NLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 19, 31.1390))) + val position2 = Position("04:54:49".hours, "-05 34 43".deg, LNG, NLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 19, 46.2383))) + val position3 = Position("04:13:55".hours, "-05 36 32".deg, LNG, NLAT, UTC(TimeYMDHMS(2024, 2, 11, 1, 20, 1.6394))) + val initialFrame = PlateSolution(true, 0.0, 1.0.arcsec, "04:13:55".hours, "-05 36 32".deg, 1280.0, 1024.0) + val pe = PolarErrorDetermination(initialFrame, position1, position2, position3, LNG, NLAT) + val (az, alt) = pe.compute() + + // Calculated Error: Az: -00° 09' 58", Alt: 00° 04' 51", Tot: 00° 11' 05" + az.toArcsec shouldBe (-598.0 plusOrMinus 7.0) + alt.toArcsec shouldBe (291.0 plusOrMinus 7.0) } companion object { diff --git a/nebulosa-alpaca-api/src/test/kotlin/AlpacaServiceTest.kt b/nebulosa-alpaca-api/src/test/kotlin/AlpacaServiceTest.kt index 462085682..3c31b157c 100644 --- a/nebulosa-alpaca-api/src/test/kotlin/AlpacaServiceTest.kt +++ b/nebulosa-alpaca-api/src/test/kotlin/AlpacaServiceTest.kt @@ -1,21 +1,22 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.nulls.shouldNotBeNull import nebulosa.alpaca.api.AlpacaService -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class AlpacaServiceTest : StringSpec() { +@NonGitHubOnly +class AlpacaServiceTest { - init { - val client = AlpacaService("http://localhost:11111/") + @Test + fun management() { + val body = CLIENT.management.configuredDevices().execute().body().shouldNotBeNull() - "management" { - val body = client.management.configuredDevices().execute().body().shouldNotBeNull() - - for (device in body.value) { - println(device) - } + for (device in body.value) { + println(device) } } + + companion object { + + @JvmStatic val CLIENT = AlpacaService("http://localhost:11111/") + } } diff --git a/nebulosa-alpaca-discovery-protocol/src/test/kotlin/AlpacaDiscoveryProtocolTest.kt b/nebulosa-alpaca-discovery-protocol/src/test/kotlin/AlpacaDiscoveryProtocolTest.kt index 515dc40f5..755f32658 100644 --- a/nebulosa-alpaca-discovery-protocol/src/test/kotlin/AlpacaDiscoveryProtocolTest.kt +++ b/nebulosa-alpaca-discovery-protocol/src/test/kotlin/AlpacaDiscoveryProtocolTest.kt @@ -1,21 +1,19 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import nebulosa.alpaca.discovery.AlpacaDiscoveryProtocol import nebulosa.alpaca.discovery.DiscoveryListener -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test import java.net.InetAddress import kotlin.concurrent.thread -@EnabledIf(NonGitHubOnlyCondition::class) -class AlpacaDiscoveryProtocolTest : StringSpec(), DiscoveryListener { +@NonGitHubOnly +class AlpacaDiscoveryProtocolTest : DiscoveryListener { - init { - "discovery" { - val discoverer = AlpacaDiscoveryProtocol() - discoverer.registerDiscoveryListener(this@AlpacaDiscoveryProtocolTest) - thread { Thread.sleep(10000); discoverer.close() } - discoverer.run() - } + @Test + fun discovery() { + val discoverer = AlpacaDiscoveryProtocol() + discoverer.registerDiscoveryListener(this@AlpacaDiscoveryProtocolTest) + thread { Thread.sleep(10000); discoverer.close() } + discoverer.run() } override fun onServerFound(address: InetAddress, port: Int) { diff --git a/nebulosa-astrobin-api/src/test/kotlin/AstrobinServiceTest.kt b/nebulosa-astrobin-api/src/test/kotlin/AstrobinServiceTest.kt index 1b3379a01..9ba653df9 100644 --- a/nebulosa-astrobin-api/src/test/kotlin/AstrobinServiceTest.kt +++ b/nebulosa-astrobin-api/src/test/kotlin/AstrobinServiceTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.doubles.shouldBeExactly @@ -8,72 +6,83 @@ import io.kotest.matchers.longs.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.astrobin.api.AstrobinService import nebulosa.astrobin.api.SensorColor -import nebulosa.test.NonGitHubOnlyCondition - -@EnabledIf(NonGitHubOnlyCondition::class) -class AstrobinServiceTest : StringSpec() { - - init { - val service = AstrobinService() - - "sensors" { - val page = service.sensors(1).execute().body()!! - - page.count shouldBeExactly 469 - page.results.size shouldBeExactly 50 - - println(page.results[0]) - } - "sensor" { - val sensor = service.sensor(184).execute().body()!! - - sensor.id shouldBeExactly 184 - sensor.brandName shouldBe "Sony" - sensor.name shouldBe "IMX492 (mono)" - sensor.quantumEfficiency shouldBeExactly 90.0 - sensor.pixelSize shouldBeExactly 2.32 - sensor.pixelWidth shouldBeExactly 8240 - sensor.pixelHeight shouldBeExactly 5628 - sensor.readNoise shouldBeExactly 1.3 - sensor.fullWellCapacity shouldBeExactly 14.0 - sensor.adc shouldBeExactly 12 - sensor.color shouldBe SensorColor.MONO - sensor.cameras.toList().shouldContain(529) - } - "cameras" { - val page = service.cameras(1).execute().body()!! - - page.count shouldBeExactly 3362 - page.results.size shouldBeExactly 50 - - println(page.results[0]) - } - "camera" { - val camera = service.camera(529).execute().body()!! - - camera.id shouldBeExactly 529 - camera.brandName shouldBe "ZWO" - camera.name shouldBe "ASI294MM" - camera.cooled.shouldBeFalse() - camera.sensor shouldBeExactly 184 - } - "telescopes" { - val page = service.telescopes(1).execute().body()!! - - page.count shouldBeExactly 3813 - page.results.size shouldBeExactly 50 - - println(page.results[0]) - } - "telescope" { - val telescope = service.telescope(1097).execute().body()!! - - telescope.id shouldBeExactly 1097 - telescope.brandName shouldBe "GSO" - telescope.name shouldBe "6\" f/9 Ritchey-Chretien" - telescope.aperture shouldBeExactly 152.0 - telescope.minFocalLength shouldBeExactly 1368.0 - telescope.maxFocalLength shouldBeExactly 1368.0 - } +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test + +@NonGitHubOnly +class AstrobinServiceTest { + + @Test + fun sensors() { + val page = SERVICE.sensors(1).execute().body()!! + + page.count shouldBeExactly 469 + page.results.size shouldBeExactly 50 + } + + @Test + fun sensor() { + val sensor = SERVICE.sensor(184).execute().body()!! + + sensor.id shouldBeExactly 184 + sensor.brandName shouldBe "Sony" + sensor.name shouldBe "IMX492 (mono)" + sensor.quantumEfficiency shouldBeExactly 90.0 + sensor.pixelSize shouldBeExactly 2.32 + sensor.pixelWidth shouldBeExactly 8240 + sensor.pixelHeight shouldBeExactly 5628 + sensor.readNoise shouldBeExactly 1.3 + sensor.fullWellCapacity shouldBeExactly 14.0 + sensor.adc shouldBeExactly 12 + sensor.color shouldBe SensorColor.MONO + sensor.cameras.toList().shouldContain(529) + } + + @Test + fun cameras() { + val page = SERVICE.cameras(1).execute().body()!! + + page.count shouldBeExactly 3362 + page.results.size shouldBeExactly 50 + + println(page.results[0]) + } + + @Test + fun camera() { + val camera = SERVICE.camera(529).execute().body()!! + + camera.id shouldBeExactly 529 + camera.brandName shouldBe "ZWO" + camera.name shouldBe "ASI294MM" + camera.cooled.shouldBeFalse() + camera.sensor shouldBeExactly 184 + } + + @Test + fun telescopes() { + val page = SERVICE.telescopes(1).execute().body()!! + + page.count shouldBeExactly 3813 + page.results.size shouldBeExactly 50 + + println(page.results[0]) + } + + @Test + fun telescope() { + val telescope = SERVICE.telescope(1097).execute().body()!! + + telescope.id shouldBeExactly 1097 + telescope.brandName shouldBe "GSO" + telescope.name shouldBe "6\" f/9 Ritchey-Chretien" + telescope.aperture shouldBeExactly 152.0 + telescope.minFocalLength shouldBeExactly 1368.0 + telescope.maxFocalLength shouldBeExactly 1368.0 + } + + companion object { + + @JvmStatic private val SERVICE = AstrobinService() } } diff --git a/nebulosa-astrometrynet-jna/src/test/kotlin/LibAstrometryNetTest.kt b/nebulosa-astrometrynet-jna/src/test/kotlin/LibAstrometryNetTest.kt index a9a94ef26..b2a9d7bb5 100644 --- a/nebulosa-astrometrynet-jna/src/test/kotlin/LibAstrometryNetTest.kt +++ b/nebulosa-astrometrynet-jna/src/test/kotlin/LibAstrometryNetTest.kt @@ -1,122 +1,132 @@ import com.sun.jna.Pointer -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldNotBe import nebulosa.astrometrynet.platesolver.* -import nebulosa.test.NonGitHubOnlyCondition -import java.nio.file.Path +import nebulosa.test.NonGitHubOnly +import nebulosa.test.concat +import nebulosa.test.homeDirectory +import org.junit.jupiter.api.Test import kotlin.io.path.listDirectoryEntries import kotlin.math.hypot import kotlin.math.ln import kotlin.math.min -@EnabledIf(NonGitHubOnlyCondition::class) -class LibAstrometryNetTest : StringSpec(), Solver.RecordMatchCallback { +@NonGitHubOnly +class LibAstrometryNetTest : Solver.RecordMatchCallback { init { System.setProperty("LIBASTROMETRYNET_PATH", "/home/tiagohm/Git/astrometry.net/solver/libastrometry.so") + } - val lib = LibAstrometryNet.INSTANCE - - // http://data.astrometry.net/ - val indexDir = Path.of(System.getProperty("user.home"), "Downloads", "Index Files") + @Test + fun structureSizes() { + val tan = Tan.ByValue() + tan.size() shouldBeExactly 88 - "structure sizes" { - val tan = Tan.ByValue() - tan.size() shouldBeExactly 88 + val sip = Sip.ByValue() + sip.size() shouldBeExactly 3304 - val sip = Sip.ByValue() - sip.size() shouldBeExactly 3304 + val index = Index.ByValue() + index.size() shouldBeExactly 136 - val index = Index.ByValue() - index.size() shouldBeExactly 136 + val xylist = XYList.ByValue() + xylist.size() shouldBeExactly 72 - val xylist = XYList.ByValue() - xylist.size() shouldBeExactly 72 + val starxy = StarXY.ByValue() + starxy.size() shouldBeExactly 72 - val starxy = StarXY.ByValue() - starxy.size() shouldBeExactly 72 + val matched = Matched.ByValue() + matched.size() shouldBeExactly 752 - val matched = Matched.ByValue() - matched.size() shouldBeExactly 752 + val solver = Solver.ByValue() + solver.size() shouldBeExactly 1208 + } - val solver = Solver.ByValue() - solver.size() shouldBeExactly 1208 - } - "load index" { - for (i in 7..19) { - val index = lib.index_load("$indexDir/index-41%02d.fits".format(i), 0, null) - index.indexName shouldBe "$indexDir/index-41%02d.fits".format(i) - index.indexId shouldBeExactly 4100 + i - index.cutnsweep shouldBeExactly 10 - lib.index_close(index) - lib.index_free(index) - } + @Test + fun loadIndex() { + for (i in 7..19) { + val index = LibAstrometryNet.INSTANCE.index_load("$INDEX_DIR/index-41%02d.fits".format(i), 0, null) + index.indexName shouldBe "$INDEX_DIR/index-41%02d.fits".format(i) + index.indexId shouldBeExactly 4100 + i + index.cutnsweep shouldBeExactly 10 + LibAstrometryNet.INSTANCE.index_close(index) + LibAstrometryNet.INSTANCE.index_free(index) } - "open xyls" { - val xyls = lib.xylist_open("/home/tiagohm/Git/astrometry.net/solver/apod4.xy") - xyls.xname shouldBe "X" - xyls.yname shouldBe "Y" - xyls.xtype shouldBeExactly 8 - xyls.ytype shouldBeExactly 8 - xyls.includeFlux shouldBe LibAstrometryNet.YES - xyls.includeBackground shouldBe LibAstrometryNet.YES - } - "new solver" { - val solver = lib.solver_new() - solver shouldNotBe Pointer.NULL - lib.solver_free(solver) - } - "run solver" { - val solver = lib.solver_new() - - // https://github.com/dstndstn/astrometry.net/blob/main/solver/control-program.c - - solver.recordMatchCallback = this@LibAstrometryNetTest - solver.funitsLower = 0.1 - solver.funitsUpper = 10.0 - solver.distanceFromQuadBonus = 1 - solver.quadSizeMin = 0.1 * min(719, 507) // image width, height - solver.quadSizeMax = hypot(719.0, 507.0) - solver.doTweak = 1 - solver.tweakAbOrder = 1 - solver.tweakAbpOrder = 4 - solver.write() - - lib.solver_set_keep_logodds(solver, ln(1e12)) - - indexDir.listDirectoryEntries("*.fits").sorted().forEach { - val index = lib.index_load("$it", 0, null) - println(it) - lib.solver_add_index(solver, index) - } - - val xyls = lib.xylist_open("/home/tiagohm/Git/astrometry.net/solver/apod4.xy") - val xy = lib.xylist_read_field(xyls, null) - lib.solver_reset_counters(solver) - lib.solver_reset_best_match(solver) - lib.solver_set_field(solver, xy) - lib.solver_set_field_bounds(solver, 0.0, 719.0, 0.0, 507.0) - lib.solver_preprocess_field(solver) - solver.read() - println(solver) - lib.solver_run(solver) - solver.read() - println(solver) - - lib.solver_did_solve(solver).shouldBeTrue() - - lib.xylist_close(xyls) - lib.solver_free_field(solver) - lib.solver_free(solver) + } + + @Test + fun openXyls() { + val xyls = LibAstrometryNet.INSTANCE.xylist_open("/home/tiagohm/Git/astrometry.net/solver/apod4.xy") + xyls.xname shouldBe "X" + xyls.yname shouldBe "Y" + xyls.xtype shouldBeExactly 8 + xyls.ytype shouldBeExactly 8 + xyls.includeFlux shouldBe LibAstrometryNet.YES + xyls.includeBackground shouldBe LibAstrometryNet.YES + } + + @Test + fun newSolver() { + val solver = LibAstrometryNet.INSTANCE.solver_new() + solver shouldNotBe Pointer.NULL + LibAstrometryNet.INSTANCE.solver_free(solver) + } + + @Test + fun runSolver() { + val solver = LibAstrometryNet.INSTANCE.solver_new() + + // https://github.com/dstndstn/astrometry.net/blob/main/solver/control-program.c + + solver.recordMatchCallback = this@LibAstrometryNetTest + solver.funitsLower = 0.1 + solver.funitsUpper = 10.0 + solver.distanceFromQuadBonus = 1 + solver.quadSizeMin = 0.1 * min(719, 507) // image width, height + solver.quadSizeMax = hypot(719.0, 507.0) + solver.doTweak = 1 + solver.tweakAbOrder = 1 + solver.tweakAbpOrder = 4 + solver.write() + + LibAstrometryNet.INSTANCE.solver_set_keep_logodds(solver, ln(1e12)) + + INDEX_DIR.listDirectoryEntries("*.fits").sorted().forEach { + val index = LibAstrometryNet.INSTANCE.index_load("$it", 0, null) + println(it) + LibAstrometryNet.INSTANCE.solver_add_index(solver, index) } + + val xyls = LibAstrometryNet.INSTANCE.xylist_open("/home/tiagohm/Git/astrometry.net/solver/apod4.xy") + val xy = LibAstrometryNet.INSTANCE.xylist_read_field(xyls, null) + LibAstrometryNet.INSTANCE.solver_reset_counters(solver) + LibAstrometryNet.INSTANCE.solver_reset_best_match(solver) + LibAstrometryNet.INSTANCE.solver_set_field(solver, xy) + LibAstrometryNet.INSTANCE.solver_set_field_bounds(solver, 0.0, 719.0, 0.0, 507.0) + LibAstrometryNet.INSTANCE.solver_preprocess_field(solver) + solver.read() + println(solver) + LibAstrometryNet.INSTANCE.solver_run(solver) + solver.read() + println(solver) + + LibAstrometryNet.INSTANCE.solver_did_solve(solver).shouldBeTrue() + + LibAstrometryNet.INSTANCE.xylist_close(xyls) + LibAstrometryNet.INSTANCE.solver_free_field(solver) + LibAstrometryNet.INSTANCE.solver_free(solver) } override fun matchFound(matched: Matched.ByReference, userData: Pointer?): Byte { println(matched) return LibAstrometryNet.YES } + + companion object { + + // http://data.astrometry.net/ + @JvmStatic private val INDEX_DIR = homeDirectory.concat("Downloads", "Index Files") + } } diff --git a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/nova/NovaAstrometryNetService.kt b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/nova/NovaAstrometryNetService.kt index 6f051c649..b3cf6590e 100644 --- a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/nova/NovaAstrometryNetService.kt +++ b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/nova/NovaAstrometryNetService.kt @@ -86,6 +86,7 @@ class NovaAstrometryNetService( companion object { const val URL = "https://nova.astrometry.net/" + const val ANONYMOUS_API_KEY = "XXXXXXXX" @JvmStatic private val TEXT_PLAIN_MEDIA_TYPE = "text/plain".toMediaType() @JvmStatic private val OCTET_STREAM_MEDIA_TYPE = "application/octet-stream".toMediaType() diff --git a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/NovaAstrometryNetPlateSolver.kt b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/NovaAstrometryNetPlateSolver.kt index 0719e5d0b..ace5f2ad4 100644 --- a/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/NovaAstrometryNetPlateSolver.kt +++ b/nebulosa-astrometrynet/src/main/kotlin/nebulosa/astrometrynet/platesolver/NovaAstrometryNetPlateSolver.kt @@ -1,6 +1,7 @@ package nebulosa.astrometrynet.platesolver import nebulosa.astrometrynet.nova.NovaAstrometryNetService +import nebulosa.astrometrynet.nova.NovaAstrometryNetService.Companion.ANONYMOUS_API_KEY import nebulosa.astrometrynet.nova.Session import nebulosa.astrometrynet.nova.Upload import nebulosa.common.concurrency.cancel.CancellationToken @@ -103,8 +104,6 @@ data class NovaAstrometryNetPlateSolver( companion object { - const val ANONYMOUS_API_KEY = "XXXXXXXX" - private const val SESSION_EXPIRATION_TIME = 1000L * 60 * 15 @JvmStatic private val LOG = loggerFor() diff --git a/nebulosa-astrometrynet/src/test/kotlin/AstrometryNetServiceTest.kt b/nebulosa-astrometrynet/src/test/kotlin/AstrometryNetServiceTest.kt index e92259020..0d6496a27 100644 --- a/nebulosa-astrometrynet/src/test/kotlin/AstrometryNetServiceTest.kt +++ b/nebulosa-astrometrynet/src/test/kotlin/AstrometryNetServiceTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.ints.shouldBeExactly @@ -8,73 +7,90 @@ import io.kotest.matchers.shouldBe import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldNotBeBlank import nebulosa.astrometrynet.nova.NovaAstrometryNetService +import nebulosa.astrometrynet.nova.NovaAstrometryNetService.Companion.ANONYMOUS_API_KEY import nebulosa.astrometrynet.nova.Parity import nebulosa.astrometrynet.nova.Upload -import java.nio.file.Path +import nebulosa.test.concat +import nebulosa.test.dataDirectory +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test -class AstrometryNetServiceTest : StringSpec() { +class AstrometryNetServiceTest { - init { - val file = Path.of("../data/ldn673s_block1123.jpg") + @Test + fun login() { + val session = SERVICE.login(ANONYMOUS_API_KEY).execute().body().shouldNotBeNull() + session.status shouldBe "success" + session.session.shouldNotBeBlank() + } + + @Test + @Disabled + fun uploadFromUrl() { + val session = SERVICE.login(ANONYMOUS_API_KEY).execute().body()!!.session + + val upload = Upload( + session, "http://apod.nasa.gov/apod/image/1206/ldn673s_block1123.jpg", + scaleLower = 0.5, scaleUpper = 1.0, centerRA = 290.0, centerDEC = 11.0, radius = 2.0 + ) - val service = NovaAstrometryNetService() + val submission = SERVICE.uploadFromUrl(upload).execute().body()!! + submission.status shouldBe "success" + submission.subId shouldBeGreaterThan 0 + } - "login" { - val session = service.login("XXXXXXXX").execute().body().shouldNotBeNull() - session.status shouldBe "success" - session.session.shouldNotBeBlank() - } - "!upload from url" { - val session = service.login("XXXXXXXX").execute().body()!!.session + @Test + @Disabled + fun uploadFromFile() { + val session = SERVICE.login(ANONYMOUS_API_KEY).execute().body()!!.session + val upload = Upload(session, scaleLower = 0.5, scaleUpper = 1.0, centerRA = 290.0, centerDEC = 11.0, radius = 2.0) + val file = dataDirectory.concat("ldn673s_block1123.jpg") + val submission = SERVICE.uploadFromFile(file, upload).execute().body()!! + submission.status shouldBe "success" + submission.subId shouldBeGreaterThan 0 + } - val upload = Upload( - session, "http://apod.nasa.gov/apod/image/1206/ldn673s_block1123.jpg", - scaleLower = 0.5, scaleUpper = 1.0, centerRA = 290.0, centerDEC = 11.0, radius = 2.0 - ) + @Test + fun submissionStatus() { + val status = SERVICE.submissionStatus(7232358).execute().body()!! + status.jobs.size shouldBeExactly 1 + status.jobs[0] shouldBeExactly 7973139 + status.userImages.size shouldBeExactly 1 + status.userImages[0] shouldBeExactly 7402591 + status.jobCalibrations.size shouldBeExactly 1 + status.jobCalibrations[0].size shouldBeExactly 2 + status.jobCalibrations[0][0] shouldBeExactly 7973139 + status.jobCalibrations[0][1] shouldBeExactly 5950379 + status.user shouldBeExactly 1000 + status.started.shouldBeTrue() + status.solved.shouldBeTrue() + } - val submission = service.uploadFromUrl(upload).execute().body()!! - submission.status shouldBe "success" - submission.subId shouldBeGreaterThan 0 - } - "!upload from file" { - val session = service.login("XXXXXXXX").execute().body()!!.session + @Test + fun jobStatus() { + val status = SERVICE.jobStatus(7973139).execute().body()!! + status.status shouldBe "success" + } + + @Test + fun jobCalibration() { + val calibration = SERVICE.jobCalibration(7973139).execute().body()!! + calibration.parity shouldBe Parity.NEGATIVE + calibration.orientation shouldBeExactly 90.0397051079753 + calibration.pixScale shouldBeExactly 2.0675124414774606 + calibration.radius shouldBeExactly 0.36561535148882157 + calibration.ra shouldBeExactly 290.237669307 + calibration.dec shouldBeExactly 11.1397773954 + } + + @Test + fun wcs() { + val text = SERVICE.wcs(7973139).execute().body()!! + text.decodeToString() shouldContain "SIMPLE" + } - val upload = Upload(session, scaleLower = 0.5, scaleUpper = 1.0, centerRA = 290.0, centerDEC = 11.0, radius = 2.0) + companion object { - val submission = service.uploadFromFile(file, upload).execute().body()!! - submission.status shouldBe "success" - submission.subId shouldBeGreaterThan 0 - } - "submission status" { - val status = service.submissionStatus(7232358).execute().body()!! - status.jobs.size shouldBeExactly 1 - status.jobs[0] shouldBeExactly 7973139 - status.userImages.size shouldBeExactly 1 - status.userImages[0] shouldBeExactly 7402591 - status.jobCalibrations.size shouldBeExactly 1 - status.jobCalibrations[0].size shouldBeExactly 2 - status.jobCalibrations[0][0] shouldBeExactly 7973139 - status.jobCalibrations[0][1] shouldBeExactly 5950379 - status.user shouldBeExactly 1000 - status.started.shouldBeTrue() - status.solved.shouldBeTrue() - } - "job status" { - val status = service.jobStatus(7973139).execute().body()!! - status.status shouldBe "success" - } - "job calibration" { - val calibration = service.jobCalibration(7973139).execute().body()!! - calibration.parity shouldBe Parity.NEGATIVE - calibration.orientation shouldBeExactly 90.0397051079753 - calibration.pixScale shouldBeExactly 2.0675124414774606 - calibration.radius shouldBeExactly 0.36561535148882157 - calibration.ra shouldBeExactly 290.237669307 - calibration.dec shouldBeExactly 11.1397773954 - } - "wcs" { - val text = service.wcs(7973139).execute().body()!! - text.decodeToString() shouldContain "SIMPLE" - } + @JvmStatic private val SERVICE = NovaAstrometryNetService() } } diff --git a/nebulosa-astrometrynet/src/test/kotlin/NovaAstrometryNetPlateSolverTest.kt b/nebulosa-astrometrynet/src/test/kotlin/NovaAstrometryNetPlateSolverTest.kt index 115d16d36..dbd13b6cf 100644 --- a/nebulosa-astrometrynet/src/test/kotlin/NovaAstrometryNetPlateSolverTest.kt +++ b/nebulosa-astrometrynet/src/test/kotlin/NovaAstrometryNetPlateSolverTest.kt @@ -1,42 +1,43 @@ -import io.kotest.core.annotation.Ignored -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.shouldBeExactly import nebulosa.astrometrynet.nova.NovaAstrometryNetService import nebulosa.astrometrynet.platesolver.NovaAstrometryNetPlateSolver import nebulosa.math.deg import nebulosa.math.toArcsec import nebulosa.math.toDegrees -import java.nio.file.Path +import nebulosa.test.concat +import nebulosa.test.dataDirectory +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test -@Ignored -class NovaAstrometryNetPlateSolverTest : StringSpec() { +@Disabled +class NovaAstrometryNetPlateSolverTest { - init { - val file = Path.of("../data/ldn673s_block1123.jpg") + @Test + fun solve() { + val calibration = SOLVER.solve(FILE, null, centerRA = 290.0.deg, centerDEC = 11.0.deg, radius = 2.0.deg) - "solve" { - val service = NovaAstrometryNetService() - val solver = NovaAstrometryNetPlateSolver(service) + calibration.orientation.toDegrees shouldBeExactly 90.0397051079753 + calibration.scale.toArcsec shouldBeExactly 2.0675124414774606 + calibration.radius.toDegrees shouldBeExactly 0.36561535148882157 + calibration.rightAscension.toDegrees shouldBeExactly 290.237669307 + calibration.declination.toDegrees shouldBeExactly 11.1397773954 + } - val calibration = solver.solve(file, null, centerRA = 290.0.deg, centerDEC = 11.0.deg, radius = 2.0.deg) + @Test + fun blindSolve() { + val calibration = SOLVER.solve(FILE, null) - calibration.orientation.toDegrees shouldBeExactly 90.0397051079753 - calibration.scale.toArcsec shouldBeExactly 2.0675124414774606 - calibration.radius.toDegrees shouldBeExactly 0.36561535148882157 - calibration.rightAscension.toDegrees shouldBeExactly 290.237669307 - calibration.declination.toDegrees shouldBeExactly 11.1397773954 - } - "blind solve" { - val service = NovaAstrometryNetService() - val solver = NovaAstrometryNetPlateSolver(service) + calibration.orientation.toDegrees shouldBeExactly 90.0397051079753 + calibration.scale.toArcsec shouldBeExactly 2.0675124414774606 + calibration.radius.toDegrees shouldBeExactly 0.36561535148882157 + calibration.rightAscension.toDegrees shouldBeExactly 290.237669307 + calibration.declination.toDegrees shouldBeExactly 11.1397773954 + } - val calibration = solver.solve(file, null) + companion object { - calibration.orientation.toDegrees shouldBeExactly 90.0397051079753 - calibration.scale.toArcsec shouldBeExactly 2.0675124414774606 - calibration.radius.toDegrees shouldBeExactly 0.36561535148882157 - calibration.rightAscension.toDegrees shouldBeExactly 290.237669307 - calibration.declination.toDegrees shouldBeExactly 11.1397773954 - } + @JvmStatic private val FILE = dataDirectory.concat("ldn673s_block1123.jpg") + @JvmStatic private val SERVICE = NovaAstrometryNetService() + @JvmStatic private val SOLVER = NovaAstrometryNetPlateSolver(SERVICE) } } diff --git a/nebulosa-common/src/test/kotlin/CancellationTokenTest.kt b/nebulosa-common/src/test/kotlin/CancellationTokenTest.kt index 84688e538..c5faaad61 100644 --- a/nebulosa-common/src/test/kotlin/CancellationTokenTest.kt +++ b/nebulosa-common/src/test/kotlin/CancellationTokenTest.kt @@ -1,63 +1,70 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.shouldBe import nebulosa.common.concurrency.cancel.CancellationSource import nebulosa.common.concurrency.cancel.CancellationToken +import org.junit.jupiter.api.Test -class CancellationTokenTest : StringSpec() { +class CancellationTokenTest { - init { - "cancel" { - var source: CancellationSource? = null - val token = CancellationToken() - token.listen { source = it } - token.cancel(false) - token.get() shouldBe source - source shouldBe CancellationSource.Cancel(false) - token.isCancelled.shouldBeTrue() - token.isDone.shouldBeTrue() - } - "cancel may interrupt if running" { - var source: CancellationSource? = null - val token = CancellationToken() - token.listen { source = it } - token.cancel() - token.get() shouldBe source - source shouldBe CancellationSource.Cancel(true) - token.isCancelled.shouldBeTrue() - token.isDone.shouldBeTrue() - } - "close" { - var source: CancellationSource? = null - val token = CancellationToken() - token.listen { source = it } - token.close() - token.get() shouldBe source - source shouldBe CancellationSource.Close - token.isCancelled.shouldBeTrue() - token.isDone.shouldBeTrue() - } - "listen after cancel" { - var source: CancellationSource? = null - val token = CancellationToken() - token.cancel() - token.listen { source = it } - token.get() shouldBe CancellationSource.Cancel(true) - source shouldBe CancellationSource.Listen - token.isCancelled.shouldBeTrue() - token.isDone.shouldBeTrue() - } - "none" { - var source: CancellationSource? = null - val token = CancellationToken.NONE - token.listen { source = it } - token.cancel() - token.get() shouldBe CancellationSource.None - source.shouldBeNull() - token.isCancelled.shouldBeFalse() - token.isDone.shouldBeTrue() - } + @Test + fun cancel() { + var source: CancellationSource? = null + val token = CancellationToken() + token.listen { source = it } + token.cancel(false) + token.get() shouldBe source + source shouldBe CancellationSource.Cancel(false) + token.isCancelled.shouldBeTrue() + token.isDone.shouldBeTrue() + } + + @Test + fun cancelMayInterruptIfRunning() { + var source: CancellationSource? = null + val token = CancellationToken() + token.listen { source = it } + token.cancel() + token.get() shouldBe source + source shouldBe CancellationSource.Cancel(true) + token.isCancelled.shouldBeTrue() + token.isDone.shouldBeTrue() + } + + @Test + fun close() { + var source: CancellationSource? = null + val token = CancellationToken() + token.listen { source = it } + token.close() + token.get() shouldBe source + source shouldBe CancellationSource.Close + token.isCancelled.shouldBeTrue() + token.isDone.shouldBeTrue() + } + + @Test + fun listenAfterCancel() { + var source: CancellationSource? = null + val token = CancellationToken() + token.cancel() + token.listen { source = it } + token.get() shouldBe CancellationSource.Cancel(true) + source shouldBe CancellationSource.Listen + token.isCancelled.shouldBeTrue() + token.isDone.shouldBeTrue() + } + + @Test + fun none() { + var source: CancellationSource? = null + val token = CancellationToken.NONE + token.listen { source = it } + token.cancel() + token.get() shouldBe CancellationSource.None + source.shouldBeNull() + token.isCancelled.shouldBeFalse() + token.isDone.shouldBeTrue() } } diff --git a/nebulosa-common/src/test/kotlin/CommandLineTest.kt b/nebulosa-common/src/test/kotlin/CommandLineTest.kt index 89cea34bf..80ddb23c8 100644 --- a/nebulosa-common/src/test/kotlin/CommandLineTest.kt +++ b/nebulosa-common/src/test/kotlin/CommandLineTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContain import io.kotest.matchers.collections.shouldNotBeEmpty import io.kotest.matchers.ints.shouldBeExactly @@ -8,65 +6,71 @@ import io.kotest.matchers.longs.shouldBeGreaterThanOrEqual import io.kotest.matchers.longs.shouldBeLessThan import nebulosa.common.exec.CommandLineListener import nebulosa.common.exec.commandLine -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test import java.nio.file.Path import java.time.Duration import kotlin.concurrent.thread import kotlin.system.measureTimeMillis -@EnabledIf(NonGitHubOnlyCondition::class) -class CommandLineTest : StringSpec() { +@NonGitHubOnly +class CommandLineTest { - init { - "sleep" { - val cmd = commandLine { - executable("sleep") - putArg("2") - } - - measureTimeMillis { - cmd.start().get() shouldBeExactly 0 - } shouldBeGreaterThanOrEqual 2000 + @Test + fun sleep() { + val cmd = commandLine { + executable("sleep") + putArg("2") } - "sleep with timeout" { - val cmd = commandLine { - executable("sleep") - putArg("10") - } - measureTimeMillis { - cmd.start(Duration.ofSeconds(2)).get() shouldNotBeExactly 0 - } shouldBeGreaterThanOrEqual 2000 + measureTimeMillis { + cmd.start().get() shouldBeExactly 0 + } shouldBeGreaterThanOrEqual 2000 + } + + @Test + fun sleepWithTimeout() { + val cmd = commandLine { + executable("sleep") + putArg("10") } - "kill sleep" { - val cmd = commandLine { - executable("sleep") - putArg("10") - } - thread { Thread.sleep(2000); cmd.stop() } + measureTimeMillis { + cmd.start(Duration.ofSeconds(2)).get() shouldNotBeExactly 0 + } shouldBeGreaterThanOrEqual 2000 + } - measureTimeMillis { - cmd.start().get() shouldNotBeExactly 0 - } shouldBeGreaterThanOrEqual 2000 shouldBeLessThan 10000 + @Test + fun killSleep() { + val cmd = commandLine { + executable("sleep") + putArg("10") } - "ls" { - val lineReadListener = object : CommandLineListener.OnLineRead, ArrayList(64) { - override fun onLineRead(line: String) { - add(line) - } - } + thread { Thread.sleep(2000); cmd.stop() } + + measureTimeMillis { + cmd.start().get() shouldNotBeExactly 0 + } shouldBeGreaterThanOrEqual 2000 shouldBeLessThan 10000 + } + + @Test + fun ls() { + val lineReadListener = object : CommandLineListener.OnLineRead, ArrayList(64) { - val cmd = commandLine { - executable("ls") - workingDirectory(Path.of("../")) - registerCommandLineListener(lineReadListener) + override fun onLineRead(line: String) { + add(line) } + } - cmd.start().get() shouldBeExactly 0 - lineReadListener.shouldNotBeEmpty() - lineReadListener.shouldContain("nebulosa-image") + val cmd = commandLine { + executable("ls") + workingDirectory(Path.of("../")) + registerCommandLineListener(lineReadListener) } + + cmd.start().get() shouldBeExactly 0 + lineReadListener.shouldNotBeEmpty() + lineReadListener.shouldContain("nebulosa-image") } } diff --git a/nebulosa-common/src/test/kotlin/CountUpDownLatchTest.kt b/nebulosa-common/src/test/kotlin/CountUpDownLatchTest.kt index 5c96bf082..ad393cbd6 100644 --- a/nebulosa-common/src/test/kotlin/CountUpDownLatchTest.kt +++ b/nebulosa-common/src/test/kotlin/CountUpDownLatchTest.kt @@ -1,103 +1,113 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.longs.shouldBeGreaterThanOrEqual -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch import nebulosa.common.concurrency.latch.CountUpDownLatch +import org.junit.jupiter.api.Test import java.util.concurrent.TimeUnit +import kotlin.concurrent.thread import kotlin.system.measureTimeMillis -@Suppress("OPT_IN_USAGE") -class CountUpDownLatchTest : StringSpec() { - - init { - "count up to 1 and count down to 0" { - val latch = CountUpDownLatch() - - latch.countUp() shouldBeExactly 1 - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.countDown() shouldBeExactly 0 } - latch.await() - } shouldBeGreaterThanOrEqual 2000L - } - "count up to 2 and timeout on count down to 1" { - val latch = CountUpDownLatch() - latch.countUp(2) shouldBeExactly 2 - GlobalScope.launch { delay(2000L); latch.countDown() shouldBeExactly 1 } - latch.await(3L, TimeUnit.SECONDS).shouldBeFalse() - } - "count up to 2 and count down to 0" { - val latch = CountUpDownLatch() - - latch.countUp(2) shouldBeExactly 2 - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.countDown() shouldBeExactly 1 } - GlobalScope.launch { delay(4000L); latch.countDown() shouldBeExactly 0 } - latch.await() - } shouldBeGreaterThanOrEqual 4000L - } - "count up to 10 and reset to 0" { - val latch = CountUpDownLatch() - - latch.countUp(10) shouldBeExactly 10 - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.reset() shouldBeExactly 0 } - latch.await() - } shouldBeGreaterThanOrEqual 2000L - } - "count up to 5 and count down to 3" { - val latch = CountUpDownLatch() - - latch.countUp(5) shouldBeExactly 5 - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.countDown(2) shouldBeExactly 3 } - latch.await(3) - } shouldBeGreaterThanOrEqual 2000L - } - "count up to 5 and reset to 3" { - val latch = CountUpDownLatch() - - latch.countUp(5) shouldBeExactly 5 - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.reset(3) shouldBeExactly 3 } - latch.await(3) - } shouldBeGreaterThanOrEqual 2000L - } - "count up to 1 and count down to 0 repeatly" { - val latch = CountUpDownLatch() - - measureTimeMillis { - repeat(3) { - latch.countUp() shouldBeExactly 1 - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.countDown() shouldBeExactly 0 } - latch.await(3L, TimeUnit.SECONDS).shouldBeTrue() - } shouldBeGreaterThanOrEqual 2000L - } - } shouldBeGreaterThanOrEqual 6000L - } - "count up to n and count down to 0 repeatly" { - val latch = CountUpDownLatch() - - measureTimeMillis { - for (n in 1..10) { - latch.countUp(n) shouldBeExactly n - - measureTimeMillis { - GlobalScope.launch { delay(2000L); latch.countDown(n) shouldBeExactly 0 } - latch.await(3L, TimeUnit.SECONDS).shouldBeTrue() - } shouldBeGreaterThanOrEqual 2000L - } - } shouldBeGreaterThanOrEqual 20000L - } +class CountUpDownLatchTest { + + @Test + fun countUpTo1AndCountDownTo0() { + val latch = CountUpDownLatch() + + latch.countUp() shouldBeExactly 1 + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.countDown() shouldBeExactly 0 } + latch.await() + } shouldBeGreaterThanOrEqual 2000L + } + + @Test + fun countUpTo2AndTimeoutOnCountDownTo1() { + val latch = CountUpDownLatch() + latch.countUp(2) shouldBeExactly 2 + thread { Thread.sleep(2000L); latch.countDown() shouldBeExactly 1 } + latch.await(3L, TimeUnit.SECONDS).shouldBeFalse() + } + + @Test + fun countUpTo2AndCountDownTo0() { + val latch = CountUpDownLatch() + + latch.countUp(2) shouldBeExactly 2 + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.countDown() shouldBeExactly 1 } + thread { Thread.sleep(4000L); latch.countDown() shouldBeExactly 0 } + latch.await() + } shouldBeGreaterThanOrEqual 4000L + } + + @Test + fun countUpTo10AndResetTo0() { + val latch = CountUpDownLatch() + + latch.countUp(10) shouldBeExactly 10 + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.reset() shouldBeExactly 0 } + latch.await() + } shouldBeGreaterThanOrEqual 2000L + } + + @Test + fun countUpTo5AndCountDownTo3() { + val latch = CountUpDownLatch() + + latch.countUp(5) shouldBeExactly 5 + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.countDown(2) shouldBeExactly 3 } + latch.await(3) + } shouldBeGreaterThanOrEqual 2000L + } + + @Test + fun countUpTo5AndResetTo3() { + val latch = CountUpDownLatch() + + latch.countUp(5) shouldBeExactly 5 + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.reset(3) shouldBeExactly 3 } + latch.await(3) + } shouldBeGreaterThanOrEqual 2000L + } + + @Test + fun countUpTo1AndCountDownTo0Repeatly() { + val latch = CountUpDownLatch() + + measureTimeMillis { + repeat(3) { + latch.countUp() shouldBeExactly 1 + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.countDown() shouldBeExactly 0 } + latch.await(3L, TimeUnit.SECONDS).shouldBeTrue() + } shouldBeGreaterThanOrEqual 2000L + } + } shouldBeGreaterThanOrEqual 6000L + } + + @Test + fun countUpToNAndCountDownTo0Repeatly() { + val latch = CountUpDownLatch() + + measureTimeMillis { + for (n in 1..10) { + latch.countUp(n) shouldBeExactly n + + measureTimeMillis { + thread { Thread.sleep(2000L); latch.countDown(n) shouldBeExactly 0 } + latch.await(3L, TimeUnit.SECONDS).shouldBeTrue() + } shouldBeGreaterThanOrEqual 2000L + } + } shouldBeGreaterThanOrEqual 20000L } } diff --git a/nebulosa-common/src/test/kotlin/PauserTest.kt b/nebulosa-common/src/test/kotlin/PauserTest.kt index 45724801f..d4161dba6 100644 --- a/nebulosa-common/src/test/kotlin/PauserTest.kt +++ b/nebulosa-common/src/test/kotlin/PauserTest.kt @@ -1,30 +1,31 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import nebulosa.common.concurrency.latch.Pauser +import org.junit.jupiter.api.Test import java.util.concurrent.TimeUnit import kotlin.concurrent.thread -class PauserTest : StringSpec() { +class PauserTest { - init { - "pause and wait for unpause" { - val pauser = Pauser() - pauser.isPaused.shouldBeFalse() - pauser.pause() - pauser.isPaused.shouldBeTrue() - thread { Thread.sleep(1000); pauser.unpause() } - pauser.waitForPause() - pauser.isPaused.shouldBeFalse() - } - "pause and not wait for unpause" { - val pauser = Pauser() - pauser.isPaused.shouldBeFalse() - pauser.pause() - pauser.isPaused.shouldBeTrue() - thread { Thread.sleep(1000); pauser.unpause() } - pauser.waitForPause(500, TimeUnit.MILLISECONDS).shouldBeFalse() - pauser.isPaused.shouldBeTrue() - } + @Test + fun pauseAndWaitForUnpause() { + val pauser = Pauser() + pauser.isPaused.shouldBeFalse() + pauser.pause() + pauser.isPaused.shouldBeTrue() + thread { Thread.sleep(1000); pauser.unpause() } + pauser.waitForPause() + pauser.isPaused.shouldBeFalse() + } + + @Test + fun pauseAndNotWaitForUnpause() { + val pauser = Pauser() + pauser.isPaused.shouldBeFalse() + pauser.pause() + pauser.isPaused.shouldBeTrue() + thread { Thread.sleep(1000); pauser.unpause() } + pauser.waitForPause(500, TimeUnit.MILLISECONDS).shouldBeFalse() + pauser.isPaused.shouldBeTrue() } } diff --git a/nebulosa-curve-fitting/src/main/kotlin/nebulosa/curve/fitting/HyperbolicFitting.kt b/nebulosa-curve-fitting/src/main/kotlin/nebulosa/curve/fitting/HyperbolicFitting.kt index fe5f55bea..1c74df37f 100644 --- a/nebulosa-curve-fitting/src/main/kotlin/nebulosa/curve/fitting/HyperbolicFitting.kt +++ b/nebulosa-curve-fitting/src/main/kotlin/nebulosa/curve/fitting/HyperbolicFitting.kt @@ -1,7 +1,10 @@ package nebulosa.curve.fitting import nebulosa.math.squared -import kotlin.math.* +import kotlin.math.asinh +import kotlin.math.cosh +import kotlin.math.round +import kotlin.math.sqrt // https://bitbucket.org/Isbeorn/nina/src/master/NINA.Core.WPF/Utility/AutoFocus/HyperbolicFitting.cs diff --git a/nebulosa-curve-fitting/src/test/kotlin/AutoFocusTest.kt b/nebulosa-curve-fitting/src/test/kotlin/AutoFocusTest.kt index 003ab2617..7bf9e5558 100644 --- a/nebulosa-curve-fitting/src/test/kotlin/AutoFocusTest.kt +++ b/nebulosa-curve-fitting/src/test/kotlin/AutoFocusTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe @@ -7,91 +6,118 @@ import nebulosa.curve.fitting.CurvePoint.Companion.midPoint import nebulosa.curve.fitting.HyperbolicFitting import nebulosa.curve.fitting.QuadraticFitting import nebulosa.curve.fitting.TrendLineFitting +import org.junit.jupiter.api.Test import kotlin.math.roundToInt -class AutoFocusTest : StringSpec() { - - init { - // ASCOM Sky Simulator: The best focus is 8000. - - "ascom:hyperbolic" { - val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_ASCOM) - curve.minimum.x.roundToInt() shouldBeExactly 8031 - curve.rSquared shouldBe (0.89 plusOrMinus 1e-2) - } - "ascom:parabolic" { - val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_ASCOM) - curve.minimum.x.roundToInt() shouldBeExactly 8051 - curve.rSquared shouldBe (0.74 plusOrMinus 1e-2) - } - "ascom:trendline" { - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_ASCOM) - line.intersection.x.roundToInt() shouldBeExactly 7873 - line.rSquared shouldBe (0.99 plusOrMinus 1e-2) - } - "ascom:hyperbolic + trendline" { - val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_ASCOM) - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_ASCOM) - (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 7952 - } - "ascom:parabolic + trendline" { - val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_ASCOM) - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_ASCOM) - (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 7962 - } - - // INDI CCD Simulator: The best focus is 36700. - - "indi:hyperbolic" { - val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - curve.minimum.x.roundToInt() shouldBeExactly 36500 - curve.rSquared shouldBe (0.84 plusOrMinus 1e-2) - } - "indi:parabolic" { - val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - curve.minimum.x.roundToInt() shouldBeExactly 36829 - curve.rSquared shouldBe (0.99 plusOrMinus 1e-2) - } - "indi:trendline" { - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - line.intersection.x.roundToInt() shouldBeExactly 39445 - line.rSquared shouldBe (0.99 plusOrMinus 1e-2) - } - "indi:hyperbolic + trendline" { - val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 37973 - } - "indi:parabolic + trendline" { - val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) - (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 38137 - } - "indi:hyperbolic:without weights" { - val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI) - curve.minimum.x.roundToInt() shouldBeExactly 36000 - curve.rSquared shouldBe (0.95 plusOrMinus 1e-2) - } - "indi:parabolic:without weights" { - val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI) - curve.minimum.x.roundToInt() shouldBeExactly 36815 - curve.rSquared shouldBe (0.99 plusOrMinus 1e-2) - } - "indi:trendline:without weights" { - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI) - line.intersection.x.roundToInt() shouldBeExactly 39653 - line.rSquared shouldBe (0.99 plusOrMinus 1e-2) - } - "indi:hyperbolic + trendline:without weights" { - val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI) - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI) - (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 37826 - } - "indi:parabolic + trendline:without weights" { - val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI) - val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI) - (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 38234 - } +class AutoFocusTest { + + // ASCOM Sky Simulator: The best focus is 8000. + + @Test + fun ascomHyperbolic() { + val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_ASCOM) + curve.minimum.x.roundToInt() shouldBeExactly 8031 + curve.rSquared shouldBe (0.89 plusOrMinus 1e-2) + } + + @Test + fun ascomParabolic() { + val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_ASCOM) + curve.minimum.x.roundToInt() shouldBeExactly 8051 + curve.rSquared shouldBe (0.74 plusOrMinus 1e-2) + } + + @Test + fun ascomTrendline() { + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_ASCOM) + line.intersection.x.roundToInt() shouldBeExactly 7873 + line.rSquared shouldBe (0.99 plusOrMinus 1e-2) + } + + @Test + fun ascomHyperbolicAndTrendline() { + val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_ASCOM) + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_ASCOM) + (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 7952 + } + + @Test + fun ascomParabolicAndTrendline() { + val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_ASCOM) + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_ASCOM) + (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 7962 + } + + // INDI CCD Simulator: The best focus is 36700. + + @Test + fun indiHyperbolic() { + val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + curve.minimum.x.roundToInt() shouldBeExactly 36500 + curve.rSquared shouldBe (0.84 plusOrMinus 1e-2) + } + + @Test + fun indiParabolic() { + val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + curve.minimum.x.roundToInt() shouldBeExactly 36829 + curve.rSquared shouldBe (0.99 plusOrMinus 1e-2) + } + + @Test + fun indiTrendline() { + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + line.intersection.x.roundToInt() shouldBeExactly 39445 + line.rSquared shouldBe (0.99 plusOrMinus 1e-2) + } + + @Test + fun indiHyperbolicAndTrendline() { + val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 37973 + } + + @Test + fun indiParabolicAndTrendline() { + val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI_WITH_WEIGHTS) + (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 38137 + } + + @Test + fun indiHyperbolicWithoutWeights() { + val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI) + curve.minimum.x.roundToInt() shouldBeExactly 36000 + curve.rSquared shouldBe (0.95 plusOrMinus 1e-2) + } + + @Test + fun indiParabolicWithoutWeights() { + val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI) + curve.minimum.x.roundToInt() shouldBeExactly 36815 + curve.rSquared shouldBe (0.99 plusOrMinus 1e-2) + } + + @Test + fun indiTrendlineWithoutWeights() { + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI) + line.intersection.x.roundToInt() shouldBeExactly 39653 + line.rSquared shouldBe (0.99 plusOrMinus 1e-2) + } + + @Test + fun indiHyperbolicAndTrendlineWithoutWeights() { + val curve = HyperbolicFitting.calculate(FOCUS_POINTS_BY_INDI) + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI) + (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 37826 + } + + @Test + fun indiParabolicAndTrendlineWithoutWeights() { + val curve = QuadraticFitting.calculate(FOCUS_POINTS_BY_INDI) + val line = TrendLineFitting.calculate(FOCUS_POINTS_BY_INDI) + (curve.minimum midPoint line.intersection).x.roundToInt() shouldBeExactly 38234 } companion object { diff --git a/nebulosa-curve-fitting/src/test/kotlin/HyperbolicFittingTest.kt b/nebulosa-curve-fitting/src/test/kotlin/HyperbolicFittingTest.kt index 22830f606..375a80b7a 100644 --- a/nebulosa-curve-fitting/src/test/kotlin/HyperbolicFittingTest.kt +++ b/nebulosa-curve-fitting/src/test/kotlin/HyperbolicFittingTest.kt @@ -1,31 +1,32 @@ import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.curve.fitting.CurveFitting import nebulosa.curve.fitting.HyperbolicFitting +import org.junit.jupiter.api.Test -class HyperbolicFittingTest : StringSpec(), CurveFitting by HyperbolicFitting { +class HyperbolicFittingTest : CurveFitting by HyperbolicFitting { - init { - "perfect V-curve with only one minimum point" { - val curve = calculate( - 1.0, 18.0, 2.0, 11.0, 3.0, 6.0, 4.0, 3.0, 5.0, 2.0, - 6.0, 3.0, 7.0, 6.0, 8.0, 11.0, 9.0, 18.0, - ) + @Test + fun perfectVCurveWithOnlyOneMinimumPoint() { + val curve = calculate( + 1.0, 18.0, 2.0, 11.0, 3.0, 6.0, 4.0, 3.0, 5.0, 2.0, + 6.0, 3.0, 7.0, 6.0, 8.0, 11.0, 9.0, 18.0, + ) - curve.minimum.x shouldBe (5.0 plusOrMinus 1e-12) - curve.minimum.y shouldBe (1.2 plusOrMinus 1e-12) - } - "bad data:prevent infinit loop" { - shouldThrow { calculate(1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } - .message shouldBe "Not enough valid data points to fit a curve." - shouldThrow { calculate(1000.0, 18.0, 1000.0, 18.0, 1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } - .message shouldBe "Not enough valid data points to fit a curve." - shouldThrow { calculate(900.0, 18.0, 1000.0, 18.0, 1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } - .message shouldBe "Not enough valid data points to fit a curve." - shouldThrow { calculate(800.0, 18.0, 900.0, 0.0, 1000.0, 0.0, 1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } - .message shouldBe "Not enough valid data points to fit a curve." - } + curve.minimum.x shouldBe (5.0 plusOrMinus 1e-12) + curve.minimum.y shouldBe (1.2 plusOrMinus 1e-12) + } + + @Test + fun badDataPreventInfinitLoop() { + shouldThrow { calculate(1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } + .message shouldBe "Not enough valid data points to fit a curve." + shouldThrow { calculate(1000.0, 18.0, 1000.0, 18.0, 1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } + .message shouldBe "Not enough valid data points to fit a curve." + shouldThrow { calculate(900.0, 18.0, 1000.0, 18.0, 1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } + .message shouldBe "Not enough valid data points to fit a curve." + shouldThrow { calculate(800.0, 18.0, 900.0, 0.0, 1000.0, 0.0, 1000.0, 18.0, 1100.0, 0.0, 1200.0, 0.0) } + .message shouldBe "Not enough valid data points to fit a curve." } } diff --git a/nebulosa-curve-fitting/src/test/kotlin/QuadraticFittingTest.kt b/nebulosa-curve-fitting/src/test/kotlin/QuadraticFittingTest.kt index 6839b07b6..63667076a 100644 --- a/nebulosa-curve-fitting/src/test/kotlin/QuadraticFittingTest.kt +++ b/nebulosa-curve-fitting/src/test/kotlin/QuadraticFittingTest.kt @@ -1,25 +1,24 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.curve.fitting.CurveFitting import nebulosa.curve.fitting.QuadraticFitting +import org.junit.jupiter.api.Test -class QuadraticFittingTest : StringSpec(), CurveFitting by QuadraticFitting { +class QuadraticFittingTest : CurveFitting by QuadraticFitting { - init { - "perfect V-curve" { - // (x-5)² + 2 - val curve = calculate( - 1.0, 18.0, 2.0, 11.0, 3.0, 6.0, - 4.0, 3.0, 5.0, 2.0, 6.0, 3.0, - 7.0, 6.0, 8.0, 11.0, 9.0, 18.0, - ) + @Test + fun perfectVCurve() { + // (x-5)² + 2 + val curve = calculate( + 1.0, 18.0, 2.0, 11.0, 3.0, 6.0, + 4.0, 3.0, 5.0, 2.0, 6.0, 3.0, + 7.0, 6.0, 8.0, 11.0, 9.0, 18.0, + ) - curve(5.0) shouldBeExactly 2.0 - curve.minimum.x shouldBe (5.0 plusOrMinus 1e-12) - curve.minimum.y shouldBe (2.0 plusOrMinus 1e-12) - curve.rSquared shouldBe (1.0 plusOrMinus 1e-12) - } + curve(5.0) shouldBeExactly 2.0 + curve.minimum.x shouldBe (5.0 plusOrMinus 1e-12) + curve.minimum.y shouldBe (2.0 plusOrMinus 1e-12) + curve.rSquared shouldBe (1.0 plusOrMinus 1e-12) } } diff --git a/nebulosa-curve-fitting/src/test/kotlin/TrendLineFittingTest.kt b/nebulosa-curve-fitting/src/test/kotlin/TrendLineFittingTest.kt index 57ed6bd11..3ee0b1672 100644 --- a/nebulosa-curve-fitting/src/test/kotlin/TrendLineFittingTest.kt +++ b/nebulosa-curve-fitting/src/test/kotlin/TrendLineFittingTest.kt @@ -1,31 +1,32 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.curve.fitting.CurveFitting import nebulosa.curve.fitting.TrendLineFitting +import org.junit.jupiter.api.Test -class TrendLineFittingTest : StringSpec(), CurveFitting by TrendLineFitting { +class TrendLineFittingTest : CurveFitting by TrendLineFitting { - init { - "perfect V-curve with only one minimum point" { - val curve = calculate( - 1.0, 10.0, 2.0, 8.0, 3.0, 6.0, 4.0, 4.0, // left - 9.0, 10.0, 8.0, 8.0, 7.0, 6.0, 6.0, 4.0, // right - 5.0, 2.0, // tip - ) + @Test + fun perfectVCurveWithOnlyOneMinimumPoint() { + val curve = calculate( + 1.0, 10.0, 2.0, 8.0, 3.0, 6.0, 4.0, 4.0, // left + 9.0, 10.0, 8.0, 8.0, 7.0, 6.0, 6.0, 4.0, // right + 5.0, 2.0, // tip + ) - curve.intersection.x shouldBe (5.0 plusOrMinus 1e-12) - curve.intersection.y shouldBe (2.0 plusOrMinus 1e-12) - } - "perfect V-curve with flat tip with multiple points" { - val curve = calculate( - 1.0, 10.0, 2.0, 8.0, 3.0, 6.0, 4.0, 4.0, // left - 11.0, 10.0, 10.0, 8.0, 9.0, 6.0, 8.0, 4.0, // right - 5.0, 2.1, 6.0, 2.0, 7.0, 2.1, // tip - ) + curve.intersection.x shouldBe (5.0 plusOrMinus 1e-12) + curve.intersection.y shouldBe (2.0 plusOrMinus 1e-12) + } + + @Test + fun perfectVCurveWithFlatTipWithMultiplePoints() { + val curve = calculate( + 1.0, 10.0, 2.0, 8.0, 3.0, 6.0, 4.0, 4.0, // left + 11.0, 10.0, 10.0, 8.0, 9.0, 6.0, 8.0, 4.0, // right + 5.0, 2.1, 6.0, 2.0, 7.0, 2.1, // tip + ) - curve.intersection.x shouldBe (6.0 plusOrMinus 1e-12) - curve.intersection.y shouldBe (0.0 plusOrMinus 1e-12) - } + curve.intersection.x shouldBe (6.0 plusOrMinus 1e-12) + curve.intersection.y shouldBe (0.0 plusOrMinus 1e-12) } } diff --git a/nebulosa-curve-fitting/src/test/kotlin/TrendLineTest.kt b/nebulosa-curve-fitting/src/test/kotlin/TrendLineTest.kt index 96f7a5e2d..540307650 100644 --- a/nebulosa-curve-fitting/src/test/kotlin/TrendLineTest.kt +++ b/nebulosa-curve-fitting/src/test/kotlin/TrendLineTest.kt @@ -1,105 +1,116 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.curve.fitting.CurvePoint import nebulosa.curve.fitting.TrendLine +import org.junit.jupiter.api.Test -class TrendLineTest : StringSpec() { - - init { - "no points" { - val line = TrendLine.ZERO - - line.slope shouldBeExactly 0.0 - line.intercept shouldBeExactly 0.0 - } - "one point" { - val line = TrendLine(5.0, 5.0) - - line.slope shouldBeExactly 0.0 - line.intercept shouldBeExactly 0.0 - } - "two points" { - val line = TrendLine(0.0, 0.0, 1.0, 1.0) - - line.slope shouldBe (1.0 plusOrMinus 1e-12) - line.intercept shouldBe (0.0 plusOrMinus 1e-12) - } - "multiple points" { - val line = TrendLine(1.0, 10.0, 2.0, 8.0, 3.0, 6.0, 4.0, 4.0) - - line.slope shouldBe (-2.0 plusOrMinus 1e-12) - line.intercept shouldBe (12.0 plusOrMinus 1e-12) - } - "simple weighted regression" { - val line = TrendLine( - CurvePoint(1.0, 1.0, 1.0), - CurvePoint(2.0, 3.0, 1.0), - CurvePoint(3.0, 2.0, 1.0), - CurvePoint(4.0, 5.0, 2.0), - CurvePoint(5.0, 4.0, 2.0), - ) - - line.slope shouldBe (0.7812 plusOrMinus 1e-4) - line.intercept shouldBe (0.75 plusOrMinus 1e-2) - // line.rSquared shouldBeExactly 0.61 - } - "weighted regression with heteroscedastic data" { - // np.random.seed(0) - // X = np.linspace(1, 10, 100) - // Y = 2 * X + 1 + np.random.normal(0, X) - // W = 1 / X - - val x = doubleArrayOf( - 1.0, 1.47368421, 1.94736842, 2.42105263, 2.89473684, 3.36842105, - 3.84210526, 4.31578947, 4.78947368, 5.26315789, 5.73684211, 6.21052632, - 6.68421053, 7.15789474, 7.63157895, 8.10526316, 8.57894737, 9.05263158, - 9.52631579, 10.0 - ) - - val y = doubleArrayOf( - 4.76405235, 4.53707378, 6.80070028, 11.26742564, 12.1955626, 4.44495872, - 12.33455024, 8.9783531, 10.08458339, 13.68736054, 13.30003943, 22.45285652, - 19.45535743, 16.18672643, 19.6505352, 19.91504455, 30.97552047, 17.24804098, - 23.03501337, 12.45904261 - ) - - val weights = doubleArrayOf( - 1.0, 0.67857143, 0.51351351, 0.41304348, 0.34545455, 0.296875, - 0.26027397, 0.23170732, 0.20879121, 0.19, 0.17431193, 0.16101695, - 0.1496063, 0.13970588, 0.13103448, 0.12337662, 0.11656442, 0.11046512, - 0.10497238, 0.1 - ) - - val points = x.indices.map { CurvePoint(x[it], y[it], weights[it]) } - val line = TrendLine(points) - - line.slope shouldBe (2.0541 plusOrMinus 1e-4) - line.intercept shouldBe (2.8909 plusOrMinus 1e-4) - // line.rSquared shouldBeExactly 0.725 - } - "standard OLS regression" { - val x = doubleArrayOf( - 1.0, 1.47368421, 1.94736842, 2.42105263, 2.89473684, 3.36842105, - 3.84210526, 4.31578947, 4.78947368, 5.26315789, 5.73684211, 6.21052632, - 6.68421053, 7.15789474, 7.63157895, 8.10526316, 8.57894737, 9.05263158, - 9.52631579, 10.0 - ) - - val y = doubleArrayOf( - 4.76405235, 4.53707378, 6.80070028, 11.26742564, 12.1955626, 4.44495872, - 12.33455024, 8.9783531, 10.08458339, 13.68736054, 13.30003943, 22.45285652, - 19.45535743, 16.18672643, 19.6505352, 19.91504455, 30.97552047, 17.24804098, - 23.03501337, 12.45904261 - ) - - val points = x.indices.map { CurvePoint(x[it], y[it]) } - val line = TrendLine(points) - - line.slope shouldBe (1.9229 plusOrMinus 1e-4) - line.intercept shouldBe (3.6128 plusOrMinus 1e-4) - // line.rSquared shouldBeExactly 0.595 - } +class TrendLineTest { + + @Test + fun noPoints() { + val line = TrendLine.ZERO + + line.slope shouldBeExactly 0.0 + line.intercept shouldBeExactly 0.0 + } + + @Test + fun onePoint() { + val line = TrendLine(5.0, 5.0) + + line.slope shouldBeExactly 0.0 + line.intercept shouldBeExactly 0.0 + } + + @Test + fun twoPoints() { + val line = TrendLine(0.0, 0.0, 1.0, 1.0) + + line.slope shouldBe (1.0 plusOrMinus 1e-12) + line.intercept shouldBe (0.0 plusOrMinus 1e-12) + } + + @Test + fun multiplePoints() { + val line = TrendLine(1.0, 10.0, 2.0, 8.0, 3.0, 6.0, 4.0, 4.0) + + line.slope shouldBe (-2.0 plusOrMinus 1e-12) + line.intercept shouldBe (12.0 plusOrMinus 1e-12) + } + + @Test + fun simpleWeightedRegression() { + val line = TrendLine( + CurvePoint(1.0, 1.0, 1.0), + CurvePoint(2.0, 3.0, 1.0), + CurvePoint(3.0, 2.0, 1.0), + CurvePoint(4.0, 5.0, 2.0), + CurvePoint(5.0, 4.0, 2.0), + ) + + line.slope shouldBe (0.7812 plusOrMinus 1e-4) + line.intercept shouldBe (0.75 plusOrMinus 1e-2) + // line.rSquared shouldBeExactly 0.61 + } + + @Test + fun weightedRegressionWithHeteroscedasticData() { + // np.random.seed(0) + // X = np.linspace(1, 10, 100) + // Y = 2 * X + 1 + np.random.normal(0, X) + // W = 1 / X + + val x = doubleArrayOf( + 1.0, 1.47368421, 1.94736842, 2.42105263, 2.89473684, 3.36842105, + 3.84210526, 4.31578947, 4.78947368, 5.26315789, 5.73684211, 6.21052632, + 6.68421053, 7.15789474, 7.63157895, 8.10526316, 8.57894737, 9.05263158, + 9.52631579, 10.0 + ) + + val y = doubleArrayOf( + 4.76405235, 4.53707378, 6.80070028, 11.26742564, 12.1955626, 4.44495872, + 12.33455024, 8.9783531, 10.08458339, 13.68736054, 13.30003943, 22.45285652, + 19.45535743, 16.18672643, 19.6505352, 19.91504455, 30.97552047, 17.24804098, + 23.03501337, 12.45904261 + ) + + val weights = doubleArrayOf( + 1.0, 0.67857143, 0.51351351, 0.41304348, 0.34545455, 0.296875, + 0.26027397, 0.23170732, 0.20879121, 0.19, 0.17431193, 0.16101695, + 0.1496063, 0.13970588, 0.13103448, 0.12337662, 0.11656442, 0.11046512, + 0.10497238, 0.1 + ) + + val points = x.indices.map { CurvePoint(x[it], y[it], weights[it]) } + val line = TrendLine(points) + + line.slope shouldBe (2.0541 plusOrMinus 1e-4) + line.intercept shouldBe (2.8909 plusOrMinus 1e-4) + // line.rSquared shouldBeExactly 0.725 + } + + @Test + fun standardOlsRegression() { + val x = doubleArrayOf( + 1.0, 1.47368421, 1.94736842, 2.42105263, 2.89473684, 3.36842105, + 3.84210526, 4.31578947, 4.78947368, 5.26315789, 5.73684211, 6.21052632, + 6.68421053, 7.15789474, 7.63157895, 8.10526316, 8.57894737, 9.05263158, + 9.52631579, 10.0 + ) + + val y = doubleArrayOf( + 4.76405235, 4.53707378, 6.80070028, 11.26742564, 12.1955626, 4.44495872, + 12.33455024, 8.9783531, 10.08458339, 13.68736054, 13.30003943, 22.45285652, + 19.45535743, 16.18672643, 19.6505352, 19.91504455, 30.97552047, 17.24804098, + 23.03501337, 12.45904261 + ) + + val points = x.indices.map { CurvePoint(x[it], y[it]) } + val line = TrendLine(points) + + line.slope shouldBe (1.9229 plusOrMinus 1e-4) + line.intercept shouldBe (3.6128 plusOrMinus 1e-4) + // line.rSquared shouldBeExactly 0.595 } } diff --git a/nebulosa-erfa/src/test/kotlin/ErfaTest.kt b/nebulosa-erfa/src/test/kotlin/ErfaTest.kt index 8dad7452c..a2201277a 100644 --- a/nebulosa-erfa/src/test/kotlin/ErfaTest.kt +++ b/nebulosa-erfa/src/test/kotlin/ErfaTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly @@ -8,1148 +7,1380 @@ import io.kotest.matchers.shouldBe import nebulosa.constants.TAU import nebulosa.erfa.* import nebulosa.math.* +import org.junit.jupiter.api.Test @Suppress("FloatingPointLiteralPrecision") -class ErfaTest : StringSpec() { - - init { - "eraRx" { - val r = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val s = r.rotateX(0.3456789.rad) - - s[0] shouldBe (2.0 plusOrMinus 1e-12) - s[1] shouldBe (3.0 plusOrMinus 1e-12) - s[2] shouldBe (2.0 plusOrMinus 1e-12) - s[3] shouldBe (3.839043388235612460 plusOrMinus 1e-12) - s[4] shouldBe (3.237033249594111899 plusOrMinus 1e-12) - s[5] shouldBe (4.516714379005982719 plusOrMinus 1e-12) - s[6] shouldBe (1.806030415924501684 plusOrMinus 1e-12) - s[7] shouldBe (3.085711545336372503 plusOrMinus 1e-12) - s[8] shouldBe (3.687721683977873065 plusOrMinus 1e-12) - - (s == Matrix3D.rotX(0.3456789.rad) * r).shouldBeTrue() - } - "eraRy" { - val r = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val s = r.rotateY(0.3456789.rad) - - s[0] shouldBe (0.8651847818978159930 plusOrMinus 1e-12) - s[1] shouldBe (1.467194920539316554 plusOrMinus 1e-12) - s[2] shouldBe (0.1875137911274457342 plusOrMinus 1e-12) - s[3] shouldBe (3.0 plusOrMinus 1e-12) - s[4] shouldBe (2.0 plusOrMinus 1e-12) - s[5] shouldBe (3.0 plusOrMinus 1e-12) - s[6] shouldBe (3.500207892850427330 plusOrMinus 1e-12) - s[7] shouldBe (4.779889022262298150 plusOrMinus 1e-12) - s[8] shouldBe (5.381899160903798712 plusOrMinus 1e-12) - - (s == Matrix3D.rotY(0.3456789.rad) * r).shouldBeTrue() - } - "eraRz" { - val r = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val s = r.rotateZ(0.3456789.rad) - - s[0] shouldBe (2.898197754208926769 plusOrMinus 1e-12) - s[1] shouldBe (3.500207892850427330 plusOrMinus 1e-12) - s[2] shouldBe (2.898197754208926769 plusOrMinus 1e-12) - s[3] shouldBe (2.144865911309686813 plusOrMinus 1e-12) - s[4] shouldBe (0.865184781897815993 plusOrMinus 1e-12) - s[5] shouldBe (2.144865911309686813 plusOrMinus 1e-12) - s[6] shouldBe (3.0 plusOrMinus 1e-12) - s[7] shouldBe (4.0 plusOrMinus 1e-12) - s[8] shouldBe (5.0 plusOrMinus 1e-12) - - (s == Matrix3D.rotZ(0.3456789.rad) * r).shouldBeTrue() - } - "eraRxr" { - val a = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val b = Matrix3D(1.0, 2.0, 2.0, 4.0, 1.0, 1.0, 3.0, 0.0, 1.0) - val c = a * b - - c[0] shouldBe (20.0 plusOrMinus 1e-12) - c[1] shouldBe (7.0 plusOrMinus 1e-12) - c[2] shouldBe (9.0 plusOrMinus 1e-12) - c[3] shouldBe (20.0 plusOrMinus 1e-12) - c[4] shouldBe (8.0 plusOrMinus 1e-12) - c[5] shouldBe (11.0 plusOrMinus 1e-12) - c[6] shouldBe (34.0 plusOrMinus 1e-12) - c[7] shouldBe (10.0 plusOrMinus 1e-12) - c[8] shouldBe (15.0 plusOrMinus 1e-12) - } - "eraC2s" { - val (theta, phi) = eraC2s(100.0.au, (-50.0).au, 25.0.au) - theta shouldBe (-0.4636476090008061162 plusOrMinus 1e-14) - phi shouldBe (0.2199879773954594463 plusOrMinus 1e-14) - } - "eraS2c" { - val c = eraS2c(3.0123, -0.999) - c[0] shouldBe (-0.5366267667260523906 plusOrMinus 1e-12) - c[1] shouldBe (0.0697711109765145365 plusOrMinus 1e-12) - c[2] shouldBe (-0.8409302618566214041 plusOrMinus 1e-12) - } - "eraP2s" { - val (theta, phi, r) = eraP2s(100.0.au, (-50.0).au, 25.0.au) - theta shouldBe (-0.4636476090008061162 plusOrMinus 1e-12) - phi shouldBe (0.2199879773954594463 plusOrMinus 1e-12) - r shouldBe (114.5643923738960002 plusOrMinus 1e-9) - } - "eraAnpm" { - eraAnpm((-4.0).rad) shouldBe (2.283185307179586477 plusOrMinus 1e-12) - } - "eraGc2Gde" { - val (e1, p1, h1) = eraGc2Gde(6378137.0, 1.0 / 298.257223563, 2e6, 3e6, 5.244e6) - e1 shouldBe (0.9827937232473290680 plusOrMinus 1e-14) - p1 shouldBe (0.97160184819075459 plusOrMinus 1e-14) - h1 shouldBe (331.4172461426059892 plusOrMinus 1e-8) - - val (e2, p2, h2) = eraGc2Gde(6378137.0, 1.0 / 298.257222101, 2e6, 3e6, 5.244e6) - e2 shouldBe (0.9827937232473290680 plusOrMinus 1e-14) - p2 shouldBe (0.97160184820607853 plusOrMinus 1e-14) - h2 shouldBe (331.41731754844348 plusOrMinus 1e-8) - - val (e3, p3, h3) = eraGc2Gde(6378135.0, 1.0 / 298.26, 2e6, 3e6, 5.244e6) - e3 shouldBe (0.9827937232473290680 plusOrMinus 1e-14) - p3 shouldBe (0.9716018181101511937 plusOrMinus 1e-14) - h3 shouldBe (333.2770726130318123 plusOrMinus 1e-8) - } - "eraGd2Gc" { - val (x1, y1, z1) = eraGd2Gce(6378137.0, 1.0 / 298.257223563, 3.1.rad, (-0.5).rad, 2500.0) - x1 shouldBe (-5599000.5577049947 plusOrMinus 1e-7) - y1 shouldBe (233011.67223479203 plusOrMinus 1e-7) - z1 shouldBe (-3040909.4706983363 plusOrMinus 1e-7) - - val (x2, y2, z2) = eraGd2Gce(6378137.0, 1.0 / 298.257222101, 3.1.rad, (-0.5).rad, 2500.0) - x2 shouldBe (-5599000.5577260984 plusOrMinus 1e-7) - y2 shouldBe (233011.6722356702949 plusOrMinus 1e-7) - z2 shouldBe (-3040909.4706095476 plusOrMinus 1e-7) - - val (x3, y3, z3) = eraGd2Gce(6378135.0, 1.0 / 298.26, 3.1.rad, (-0.5).rad, 2500.0) - x3 shouldBe (-5598998.7626301490 plusOrMinus 1e-7) - y3 shouldBe (233011.5975297822211 plusOrMinus 1e-7) - z3 shouldBe (-3040908.6861467111 plusOrMinus 1e-7) - } - "eraC2ixys" { - val m = eraC2ixys(0.5791308486706011000e-3, 0.4020579816732961219e-4, (-0.1220040848472271978e-7).rad) - m[0, 0] shouldBe (0.9999998323037157138 plusOrMinus 1e-12) - m[0, 1] shouldBe (0.5581984869168499149e-9 plusOrMinus 1e-12) - m[0, 2] shouldBe (-0.5791308491611282180e-3 plusOrMinus 1e-12) - m[1, 0] shouldBe (-0.2384261642670440317e-7 plusOrMinus 1e-12) - m[1, 1] shouldBe (0.9999999991917468964 plusOrMinus 1e-12) - m[1, 2] shouldBe (-0.4020579110169668931e-4 plusOrMinus 1e-12) - m[2, 0] shouldBe (0.5791308486706011000e-3 plusOrMinus 1e-12) - m[2, 1] shouldBe (0.4020579816732961219e-4 plusOrMinus 1e-12) - m[2, 2] shouldBe (0.9999998314954627590 plusOrMinus 1e-12) - } - "eraPom00" { - val m = eraPom00(2.55060238e-7.rad, 1.860359247e-6.rad, (-0.1367174580728891460e-10).rad) - m[0, 0] shouldBe (0.9999999999999674721 plusOrMinus 1e-12) - m[0, 1] shouldBe (-0.1367174580728846989e-10 plusOrMinus 1e-12) - m[0, 2] shouldBe (0.2550602379999972345e-6 plusOrMinus 1e-12) - m[1, 0] shouldBe (0.1414624947957029801e-10 plusOrMinus 1e-12) - m[1, 1] shouldBe (0.9999999999982695317 plusOrMinus 1e-12) - m[1, 2] shouldBe (-0.1860359246998866389e-5 plusOrMinus 1e-12) - m[2, 0] shouldBe (-0.2550602379741215021e-6 plusOrMinus 1e-12) - m[2, 1] shouldBe (0.1860359247002414021e-5 plusOrMinus 1e-12) - m[2, 2] shouldBe (0.9999999999982370039 plusOrMinus 1e-12) - } - "eraApcs" { - val astro = eraApcs( - 2456384.5, 0.970031644, - Vector3D(-1836024.09, 1056607.7, -5998795.26), - Vector3D(-77.0361767, -133.310856, 0.0971855934), - Vector3D(-0.974170438, -0.211520082, -0.0917583024), - Vector3D(0.00364365824, -0.0154287319, -0.00668922024), - Vector3D(-0.973458265, -0.209215307, -0.0906996477), - ) - - astro.pmt shouldBe (13.25248468622587269 plusOrMinus 1e-11) - astro.eb.x shouldBe (-0.9741827110629881886 plusOrMinus 1e-12) - astro.eb.y shouldBe (-0.2115130190136415986 plusOrMinus 1e-12) - astro.eb.z shouldBe (-0.09179840186954412099 plusOrMinus 1e-12) - astro.eh.x shouldBe (-0.9736425571689454706 plusOrMinus 1e-12) - astro.eh.y shouldBe (-0.2092452125850435930 plusOrMinus 1e-12) - astro.eh.z shouldBe (-0.09075578152248299218 plusOrMinus 1e-12) - astro.em shouldBe (0.9998233241709796859 plusOrMinus 1e-12) - astro.v.x shouldBe (0.2078704993282685510e-4 plusOrMinus 1e-16) - astro.v.y shouldBe (-0.8955360106989405683e-4 plusOrMinus 1e-16) - astro.v.z shouldBe (-0.3863338994289409097e-4 plusOrMinus 1e-16) - astro.bm1 shouldBe (0.9999999950277561237 plusOrMinus 1e-12) - } - "eraApco" { - val astro = eraApco( - 2456384.5, 0.970031644, - Vector3D(-0.974170438, -0.211520082, -0.0917583024), - Vector3D(0.00364365824, -0.0154287319, -0.00668922024), - Vector3D(-0.973458265, -0.209215307, -0.0906996477), - 0.0013122272, -2.92808623e-5, 3.05749468e-8.rad, - 3.14540971.rad, (-0.527800806).rad, (-1.2345856).rad, - 2738.0, - 2.47230737e-7.rad, 1.82640464e-6.rad, (-3.01974337e-11).rad, - 0.000201418779.rad, (-2.36140831e-7).rad, - ) - - astro.pmt shouldBe (13.25248468622587269 plusOrMinus 1e-11) - astro.eb.x shouldBe (-0.9741827110630322720 plusOrMinus 1e-12) - astro.eb.y shouldBe (-0.2115130190135344832 plusOrMinus 1e-12) - astro.eb.z shouldBe (-0.09179840186949532298 plusOrMinus 1e-12) - astro.eh.x shouldBe (-0.9736425571689739035 plusOrMinus 1e-12) - astro.eh.y shouldBe (-0.2092452125849330936 plusOrMinus 1e-12) - astro.eh.z shouldBe (-0.09075578152243272599 plusOrMinus 1e-12) - astro.em shouldBe (0.9998233241709957653 plusOrMinus 1e-12) - astro.v.x shouldBe (0.2078704992916728762e-4 plusOrMinus 1e-16) - astro.v.y shouldBe (-0.8955360107151952319e-4 plusOrMinus 1e-16) - astro.v.z shouldBe (-0.3863338994288951082e-4 plusOrMinus 1e-16) - astro.bm1 shouldBe (0.9999999950277561236 plusOrMinus 1e-12) - astro.bpn[0, 0] shouldBe (0.9999991390295159156 plusOrMinus 1e-12) - astro.bpn[1, 0] shouldBe (0.4978650072505016932e-7 plusOrMinus 1e-12) - astro.bpn[2, 0] shouldBe (0.1312227200000000000e-2 plusOrMinus 1e-12) - astro.bpn[0, 1] shouldBe (-0.1136336653771609630e-7 plusOrMinus 1e-12) - astro.bpn[1, 1] shouldBe (0.9999999995713154868 plusOrMinus 1e-12) - astro.bpn[2, 1] shouldBe (-0.2928086230000000000e-4 plusOrMinus 1e-12) - astro.bpn[0, 2] shouldBe (-0.1312227200895260194e-2 plusOrMinus 1e-12) - astro.bpn[1, 2] shouldBe (0.2928082217872315680e-4 plusOrMinus 1e-12) - astro.bpn[2, 2] shouldBe (0.9999991386008323373 plusOrMinus 1e-12) - astro.along shouldBe (-0.5278008060295995734 plusOrMinus 1e-12) - astro.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) - astro.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) - astro.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) - astro.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) - astro.eral shouldBe (2.617608903970400427 plusOrMinus 1e-12) - astro.refa shouldBe (0.2014187790000000000e-3 plusOrMinus 1e-17) - astro.refb shouldBe (-0.2361408310000000000e-6 plusOrMinus 1e-18) - astro.diurab shouldBeExactly 0.0 - } - "eraSp00" { - eraSp00(2400000.5, 52541.0) shouldBe (-0.6216698469981019309e-11 plusOrMinus 1e-12) - } - "eraObl06" { - eraObl06(2400000.5, 54388.0) shouldBe (0.4090749229387258204 plusOrMinus 1e-14) - } - "eraPfw06" { - val (gamb, phib, psib, epsa) = eraPfw06(2400000.5, 50123.9999) - gamb shouldBe (-0.2243387670997995690e-5 plusOrMinus 1e-16) - phib shouldBe (0.4091014602391312808 plusOrMinus 1e-12) - psib shouldBe (-0.9501954178013031895e-3 plusOrMinus 1e-14) - epsa shouldBe (0.4091014316587367491 plusOrMinus 1e-12) - } - "eraFal03" { - eraFal03(0.80) shouldBe (5.132369751108684150 plusOrMinus 1e-12) - } - "eraFaf03" { - eraFaf03(0.80) shouldBe (0.2597711366745499518 plusOrMinus 1e-12) - } - "eraFaom03" { - eraFaom03(0.80) shouldBe (TAU - 5.973618440951302183 plusOrMinus 1e-12) - } - "eraFapa03" { - eraFapa03(0.80) shouldBe (0.1950884762240000000e-1 plusOrMinus 1e-12) - } - "eraFame03" { - eraFame03(0.80) shouldBe (5.417338184297289661 plusOrMinus 1e-12) - } - "eraFave03" { - eraFave03(0.80) shouldBe (3.424900460533758000 plusOrMinus 1e-12) - } - "eraFae03" { - eraFae03(0.80) shouldBe (1.744713738913081846 plusOrMinus 1e-12) - } - "eraFama03" { - eraFama03(0.80) shouldBe (3.275506840277781492 plusOrMinus 1e-12) - } - "eraFaju03" { - eraFaju03(0.80) shouldBe (5.275711665202481138 plusOrMinus 1e-12) - } - "eraFasa03" { - eraFasa03(0.80) shouldBe (5.371574539440827046 plusOrMinus 1e-12) - } - "eraFaur03" { - eraFaur03(0.80) shouldBe (5.180636450180413523 plusOrMinus 1e-12) - } - "eraFw2m" { - val m = eraFw2m((-0.2243387670997992368e-5).rad, 0.4091014602391312982.rad, (-0.9501954178013015092e-3).rad, 0.4091014316587367472.rad) - m[0, 0] shouldBe (0.9999995505176007047 plusOrMinus 1e-12) - m[0, 1] shouldBe (0.8695404617348192957e-3 plusOrMinus 1e-12) - m[0, 2] shouldBe (0.3779735201865582571e-3 plusOrMinus 1e-12) - m[1, 0] shouldBe (-0.8695404723772016038e-3 plusOrMinus 1e-12) - m[1, 1] shouldBe (0.9999996219496027161 plusOrMinus 1e-12) - m[1, 2] shouldBe (-0.1361752496887100026e-6 plusOrMinus 1e-12) - m[2, 0] shouldBe (-0.3779734957034082790e-3 plusOrMinus 1e-12) - m[2, 1] shouldBe (-0.1924880848087615651e-6 plusOrMinus 1e-12) - m[2, 2] shouldBe (0.9999999285679971958 plusOrMinus 1e-12) - } - "era00" { - era00(2400000.0 + 54388.0, 0.5) shouldBe (0.4022837240028158102 plusOrMinus 1e-12) - } - "eraRefco" { - val (refa, refb) = eraRefco(800.0, 10.0, 0.9, 0.4) - refa shouldBe (0.2264949956241415009e-3 plusOrMinus 1e-15) - refb shouldBe (-0.2598658261729343970e-6 plusOrMinus 1e-18) - } - "eraApcg" { - val astrom = eraApcg( - 2456165.5, 0.401182685, - Vector3D(0.901310875, -0.417402664, -0.180982288), - Vector3D(0.00742727954, 0.0140507459, 0.00609045792), - Vector3D(0.903358544, -0.415395237, -0.180084014), - ) - - astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) - astrom.eb.x shouldBe (0.901310875 plusOrMinus 1e-12) - astrom.eb.y shouldBe (-0.417402664 plusOrMinus 1e-12) - astrom.eb.z shouldBe (-0.180982288 plusOrMinus 1e-12) - astrom.eh.x shouldBe (0.8940025429324143045 plusOrMinus 1e-12) - astrom.eh.y shouldBe (-0.4110930268679817955 plusOrMinus 1e-12) - astrom.eh.z shouldBe (-0.1782189004872870264 plusOrMinus 1e-12) - astrom.em shouldBe (1.010465295811013146 plusOrMinus 1e-12) - astrom.v.x shouldBe (0.4289638913597693554e-4 plusOrMinus 1e-16) - astrom.v.y shouldBe (0.8115034051581320575e-4 plusOrMinus 1e-16) - astrom.v.z shouldBe (0.3517555136380563427e-4 plusOrMinus 1e-16) - astrom.bm1 shouldBe (0.9999999951686012981 plusOrMinus 1e-12) - } - "eraEpv00" { - val (h, b) = eraEpv00(2400000.5, 53411.52501161) - h.position[0] shouldBe (-0.7757238809297706813 plusOrMinus 1e-14) - h.position[1] shouldBe (0.5598052241363340596 plusOrMinus 1e-14) - h.position[2] shouldBe (0.2426998466481686993 plusOrMinus 1e-14) - - h.velocity[0] shouldBe (-0.1091891824147313846e-1 plusOrMinus 1e-15) - h.velocity[1] shouldBe (-0.1247187268440845008e-1 plusOrMinus 1e-15) - h.velocity[2] shouldBe (-0.5407569418065039061e-2 plusOrMinus 1e-15) - - b.position[0] shouldBe (-0.7714104440491111971 plusOrMinus 1e-14) - b.position[1] shouldBe (0.5598412061824171323 plusOrMinus 1e-14) - b.position[2] shouldBe (0.2425996277722452400 plusOrMinus 1e-14) - - b.velocity[0] shouldBe (-0.1091874268116823295e-1 plusOrMinus 1e-15) - b.velocity[1] shouldBe (-0.1246525461732861538e-1 plusOrMinus 1e-15) - b.velocity[2] shouldBe (-0.5404773180966231279e-2 plusOrMinus 1e-15) - } - "eraApcg13" { - val astrom = eraApcg13(2456165.5, 0.401182685) - - astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) - astrom.eb.x shouldBe (0.9013108747340644755 plusOrMinus 1e-12) - astrom.eb.y shouldBe (-0.4174026640406119957 plusOrMinus 1e-12) - astrom.eb.z shouldBe (-0.1809822877867817771 plusOrMinus 1e-12) - astrom.eh.x shouldBe (0.8940025429255499549 plusOrMinus 1e-12) - astrom.eh.y shouldBe (-0.4110930268331896318 plusOrMinus 1e-12) - astrom.eh.z shouldBe (-0.1782189006019749850 plusOrMinus 1e-12) - astrom.em shouldBe (1.010465295964664178 plusOrMinus 1e-12) - astrom.v.x shouldBe (0.4289638912941341125e-4 plusOrMinus 1e-16) - astrom.v.y shouldBe (0.8115034032405042132e-4 plusOrMinus 1e-16) - astrom.v.z shouldBe (0.3517555135536470279e-4 plusOrMinus 1e-16) - astrom.bm1 shouldBe (0.9999999951686013142 plusOrMinus 1e-12) - } - "eraAe2hd" { - val (ha, dec) = eraAe2hd(5.5.rad, 1.1.rad, 0.7.rad) +class ErfaTest { + + @Test + fun eraRx() { + val r = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val s = r.rotateX(0.3456789.rad) + + s[0] shouldBe (2.0 plusOrMinus 1e-12) + s[1] shouldBe (3.0 plusOrMinus 1e-12) + s[2] shouldBe (2.0 plusOrMinus 1e-12) + s[3] shouldBe (3.839043388235612460 plusOrMinus 1e-12) + s[4] shouldBe (3.237033249594111899 plusOrMinus 1e-12) + s[5] shouldBe (4.516714379005982719 plusOrMinus 1e-12) + s[6] shouldBe (1.806030415924501684 plusOrMinus 1e-12) + s[7] shouldBe (3.085711545336372503 plusOrMinus 1e-12) + s[8] shouldBe (3.687721683977873065 plusOrMinus 1e-12) + + (s == Matrix3D.rotX(0.3456789.rad) * r).shouldBeTrue() + } - ha shouldBe (0.5933291115507309663 plusOrMinus 1E-14) - dec shouldBe (0.9613934761647817620 plusOrMinus 1E-14) - } - "eraTcbTdb" { - val (whole, fraction) = eraTcbTdb(2453750.5, 0.893019599) - whole shouldBe (2453750.5 plusOrMinus 1E-6) - fraction shouldBe (0.8928551362746343397 plusOrMinus 1E-12) - } - "eraTcgTt" { - val (whole, fraction) = eraTcgTt(2453750.5, 0.892862531) - whole shouldBe (2453750.5 plusOrMinus 1E-6) - fraction shouldBe (0.8928551387488816828 plusOrMinus 1E-12) - } - "eraTdbTcb" { - val (whole, fraction) = eraTdbTcb(2453750.5, 0.892855137) - whole shouldBe (2453750.5 plusOrMinus 1E-6) - fraction shouldBe (0.8930195997253656716 plusOrMinus 1E-12) - } - "eraTtTcg" { - val (whole, fraction) = eraTtTcg(2453750.5, 0.892482639) - whole shouldBe (2453750.5 plusOrMinus 1E-6) - fraction shouldBe (0.8924900312508587113 plusOrMinus 1E-12) - } - "eraDtDb" { - val dt = eraDtDb(2448939.5, 0.123, 0.76543, 5.0123.rad, 5525.242.km, 3190.0.km) - dt shouldBe (-0.1280368005936998991e-2 plusOrMinus 1E-15) - } - "eraPvtob" { - val (p, v) = eraPvtob(2.0.rad, 0.5.rad, 3000.0, 1e-6.rad, (-0.5e-6).rad, 1e-8.rad, 5.0.rad) - p[0] shouldBe (4225081.367071159207 plusOrMinus 1e-5) - p[1] shouldBe (3681943.215856198144 plusOrMinus 1e-5) - p[2] shouldBe (3041149.399241260785 plusOrMinus 1e-5) - v[0] shouldBe (-268.4915389365998787 plusOrMinus 1e-5) - v[1] shouldBe (308.0977983288903123 plusOrMinus 1e-5) - v[2] shouldBe (0.0 plusOrMinus 1e-5) - } - "eraNut00a" { - val (psi, eps) = eraNut00a(2400000.5, 53736.0) - psi shouldBe (-0.9630909107115518431e-5 plusOrMinus 1e-13) - eps shouldBe (0.4063239174001678710e-4 plusOrMinus 1e-13) - } - "eraNut06a" { - val (psi, eps) = eraNut06a(2400000.5, 53736.0) - psi shouldBe (-0.9630912025820308797e-5 plusOrMinus 1e-13) - eps shouldBe (0.4063238496887249798e-4 plusOrMinus 1e-13) - } - "eraPnm06a" { - val rbpn = eraPnm06a(2400000.5, 50123.9999) - - rbpn[0, 0] shouldBe (0.9999995832794205484 plusOrMinus 1e-12) - rbpn[0, 1] shouldBe (0.8372382772630962111e-3 plusOrMinus 1e-14) - rbpn[0, 2] shouldBe (0.3639684771140623099e-3 plusOrMinus 1e-14) - rbpn[1, 0] shouldBe (-0.8372533744743683605e-3 plusOrMinus 1e-14) - rbpn[1, 1] shouldBe (0.9999996486492861646 plusOrMinus 1e-12) - rbpn[1, 2] shouldBe (0.4132905944611019498e-4 plusOrMinus 1e-14) - rbpn[2, 0] shouldBe (-0.3639337469629464969e-3 plusOrMinus 1e-14) - rbpn[2, 1] shouldBe (-0.4163377605910663999e-4 plusOrMinus 1e-14) - rbpn[2, 2] shouldBe (0.9999999329094260057 plusOrMinus 1e-12) - } - "eraAb" { - val pnat = Vector3D(-0.76321968546737951, -0.60869453983060384, -0.21676408580639883) - val v = Vector3D(2.1044018893653786e-5, -8.9108923304429319e-5, -3.8633714797716569e-5) - val ppr = eraAb(pnat, v, 0.99980921395708788.au, 0.99999999506209258) - - ppr[0] shouldBe (-0.7631631094219556269 plusOrMinus 1e-12) - ppr[1] shouldBe (-0.6087553082505590832 plusOrMinus 1e-12) - ppr[2] shouldBe (-0.2167926269368471279 plusOrMinus 1e-12) - } - "eraEect00" { - val eect = eraEect00(2400000.5, 53736.0) - eect shouldBe (0.2046085004885125264e-8 plusOrMinus 1e-20) - } - "eraEra00" { - val era00 = eraEra00(2454388.0, 0.5) - era00 shouldBe (0.4022837240028158102 plusOrMinus 1e-12) - } - "eraEe00" { - val ee = eraEe00(2453736.0, 0.5, 0.4090789763356509900.rad, (-0.9630909107115582393e-5).rad) - ee shouldBe (-0.8834193235367965479e-5 plusOrMinus 1e-18) - } - "eraGmst00" { - val theta = eraGmst00(2453736.0, 0.5, 2453736.0, 0.5) - theta shouldBe (1.754174972210740592 plusOrMinus 1e-12) - } - "eraGmst06" { - val theta = eraGmst06(2453736.0, 0.5, 2453736.0, 0.5) - theta shouldBe (1.754174971870091203 plusOrMinus 1e-12) - } - "eraEe06a" { - val ee = eraEe06a(2453736.0, 0.5) - ee shouldBe (-0.8834195072043790156e-5 plusOrMinus 1e-15) - } - "eraGst06a" { - val theta = eraGst06a(2453736.0, 0.5, 2453736.0, 0.5) - theta shouldBe (1.754166137675019159 plusOrMinus 1e-12) - } - "eraGst06" { - val rnpb = Matrix3D( - 0.9999989440476103608, -0.1332881761240011518e-2, -0.5790767434730085097e-3, - 0.1332858254308954453e-2, 0.9999991109044505944, -0.4097782710401555759e-4, - 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, - ) - - val theta = eraGst06(2453736.0, 0.5, 2453736.0, 0.5, rnpb) - theta shouldBe (1.754166138018167568 plusOrMinus 1e-12) - } - "eraS06" { - val s = eraS06(2400000.5, 53736.0, 0.5791308486706011000e-3, 0.4020579816732961219e-4) - s shouldBe (-0.1220032213076463117e-7 plusOrMinus 1e-18) - } - "eraS06a" { - val s = eraS06a(2400000.5, 52541.0) - s shouldBe (-0.1340680437291812383e-7 plusOrMinus 1e-18) - } - "eraEors" { - val rnpb = Matrix3D( - 0.9999989440476103608, -0.1332881761240011518e-2, -0.5790767434730085097e-3, - 0.1332858254308954453e-2, 0.9999991109044505944, -0.4097782710401555759e-4, - 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, - ) - - val eo = eraEors(rnpb, (-0.1220040848472271978e-7).rad) - eo shouldBe (-0.1332882715130744606e-2 plusOrMinus 1e-14) - } - "eraGst00a" { - val theta = eraGst00a(2453736.0, 0.5, 2453736.0, 0.5) - theta shouldBe (1.754166138018281369 plusOrMinus 1e-12) - } - "eraGst00b" { - val theta = eraGst00b(2453736.0, 0.5) - theta shouldBe (1.754166136510680589 plusOrMinus 1e-12) - } - "eraEe00a" { - val ee = eraEe00a(2453736.0, 0.5) - ee shouldBe (-0.8834192459222588227e-5 plusOrMinus 1e-18) - } - "eraEe00b" { - val ee = eraEe00b(2453736.0, 0.5) - ee shouldBe (-0.8835700060003032831e-5 plusOrMinus 1e-18) - } - "eraPr00" { - val (dpsipr, depspr) = eraPr00(2453736.0, 0.5) - dpsipr shouldBe (-0.8716465172668347629e-7 plusOrMinus 1e-22) - depspr shouldBe (-0.7342018386722813087e-8 plusOrMinus 1e-22) - } - "eraObl80" { - val eps0 = eraObl80(2454388.0, 0.5) - eps0 shouldBe (0.4090751347643816218 plusOrMinus 1e-14) - } - "eraNut00b" { - val (dpsi, deps) = eraNut00b(2453736.0, 0.5) - dpsi shouldBe (-0.9632552291148362783e-5 plusOrMinus 1e-13) - deps shouldBe (0.4063197106621159367e-4 plusOrMinus 1e-13) - } - "eraC2tcio" { - val rc2i = Matrix3D( - 0.9999998323037164738, 0.5581526271714303683e-9, -0.5791308477073443903e-3, - -0.2384266227524722273e-7, 0.9999999991917404296, -0.4020594955030704125e-4, - 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, - ) - - val rpom = Matrix3D( - 0.9999999999999674705, -0.1367174580728847031e-10, 0.2550602379999972723e-6, - 0.1414624947957029721e-10, 0.9999999999982694954, -0.1860359246998866338e-5, - -0.2550602379741215275e-6, 0.1860359247002413923e-5, 0.9999999999982369658, - ) - - val rc2t = eraC2tcio(rc2i, 1.75283325530307.rad, rpom) - - rc2t[0] shouldBe (-0.1810332128307110439 plusOrMinus 1e-12) - rc2t[1] shouldBe (0.9834769806938470149 plusOrMinus 1e-12) - rc2t[2] shouldBe (0.6555535638685466874e-4 plusOrMinus 1e-12) - rc2t[3] shouldBe (-0.9834768134135996657 plusOrMinus 1e-12) - rc2t[4] shouldBe (-0.1810332203649448367 plusOrMinus 1e-12) - rc2t[5] shouldBe (0.5749801116141106528e-3 plusOrMinus 1e-12) - rc2t[6] shouldBe (0.5773474014081407076e-3 plusOrMinus 1e-12) - rc2t[7] shouldBe (0.3961832391772658944e-4 plusOrMinus 1e-12) - rc2t[8] shouldBe (0.9999998325501691969 plusOrMinus 1e-12) - } - "eraEcm06" { - val rm = eraEcm06(2456165.5, 0.401182685) - - rm[0, 0] shouldBe (0.9999952427708701137 plusOrMinus 1e-14) - rm[0, 1] shouldBe (-0.2829062057663042347e-2 plusOrMinus 1e-14) - rm[0, 2] shouldBe (-0.1229163741100017629e-2 plusOrMinus 1e-14) - rm[1, 0] shouldBe (0.3084546876908653562e-2 plusOrMinus 1e-14) - rm[1, 1] shouldBe (0.9174891871550392514 plusOrMinus 1e-14) - rm[1, 2] shouldBe (0.3977487611849338124 plusOrMinus 1e-14) - rm[2, 0] shouldBe (0.2488512951527405928e-5 plusOrMinus 1e-14) - rm[2, 1] shouldBe (-0.3977506604161195467 plusOrMinus 1e-14) - rm[2, 2] shouldBe (0.9174935488232863071 plusOrMinus 1e-14) - } - "eraPmat06" { - val rbp = eraPmat06(2400000.5, 50123.9999) - - rbp[0, 0] shouldBe (0.9999995505176007047 plusOrMinus 1e-12) - rbp[0, 1] shouldBe (0.8695404617348208406e-3 plusOrMinus 1e-14) - rbp[0, 2] shouldBe (0.3779735201865589104e-3 plusOrMinus 1e-14) - rbp[1, 0] shouldBe (-0.8695404723772031414e-3 plusOrMinus 1e-14) - rbp[1, 1] shouldBe (0.9999996219496027161 plusOrMinus 1e-12) - rbp[1, 2] shouldBe (-0.1361752497080270143e-6 plusOrMinus 1e-14) - rbp[2, 0] shouldBe (-0.3779734957034089490e-3 plusOrMinus 1e-14) - rbp[2, 1] shouldBe (-0.1924880847894457113e-6 plusOrMinus 1e-14) - rbp[2, 2] shouldBe (0.9999999285679971958 plusOrMinus 1e-12) - } - "eraP06e" { - val e = eraP06e(2452541.0, 0.5) - - e.eps0 shouldBe (0.4090926006005828715 plusOrMinus 1e-14) - e.psia shouldBe (0.6664369630191613431e-3 plusOrMinus 1e-14) - e.oma shouldBe (0.4090925973783255982 plusOrMinus 1e-14) - e.bpa shouldBe (0.5561149371265209445e-6 plusOrMinus 1e-14) - e.bqa shouldBe (-0.6191517193290621270e-5 plusOrMinus 1e-14) - e.pia shouldBe (0.6216441751884382923e-5 plusOrMinus 1e-14) - e.bpia shouldBe (3.052014180023779882 plusOrMinus 1e-14) - e.epsa shouldBe (0.4090864054922431688 plusOrMinus 1e-14) - e.chia shouldBe (0.1387703379530915364e-5 plusOrMinus 1e-14) - e.za shouldBe (0.2921789846651790546e-3 plusOrMinus 1e-14) - e.zetaa shouldBe (0.3178773290332009310e-3 plusOrMinus 1e-14) - e.thetaa shouldBe (0.2650932701657497181e-3 plusOrMinus 1e-14) - e.pa shouldBe (0.6651637681381016288e-3 plusOrMinus 1e-14) - e.gam shouldBe (0.1398077115963754987e-5 plusOrMinus 1e-14) - e.phi shouldBe (0.4090864090837462602 plusOrMinus 1e-14) - e.psi shouldBe (0.6664464807480920325e-3 plusOrMinus 1e-14) - } - "eraNumat" { - val rmatn = eraNumat(0.4090789763356509900.rad, (-0.9630909107115582393e-5).rad, 0.4063239174001678826e-4.rad) - - rmatn[0, 0] shouldBe (0.9999999999536227949 plusOrMinus 1e-12) - rmatn[0, 1] shouldBe (0.8836239320236250577e-5 plusOrMinus 1e-12) - rmatn[0, 2] shouldBe (0.3830833447458251908e-5 plusOrMinus 1e-12) - rmatn[1, 0] shouldBe (-0.8836083657016688588e-5 plusOrMinus 1e-12) - rmatn[1, 1] shouldBe (0.9999999991354654959 plusOrMinus 1e-12) - rmatn[1, 2] shouldBe (-0.4063240865361857698e-4 plusOrMinus 1e-12) - rmatn[2, 0] shouldBe (-0.3831192481833385226e-5 plusOrMinus 1e-12) - rmatn[2, 1] shouldBe (0.4063237480216934159e-4 plusOrMinus 1e-12) - rmatn[2, 2] shouldBe (0.9999999991671660407 plusOrMinus 1e-12) - } - "eraNum06a" { - val rmatn = eraNum06a(2453736.0, 0.5) - - rmatn[0, 0] shouldBe (0.9999999999536227668 plusOrMinus 1e-12) - rmatn[0, 1] shouldBe (0.8836241998111535233e-5 plusOrMinus 1e-12) - rmatn[0, 2] shouldBe (0.3830834608415287707e-5 plusOrMinus 1e-12) - rmatn[1, 0] shouldBe (-0.8836086334870740138e-5 plusOrMinus 1e-12) - rmatn[1, 1] shouldBe (0.9999999991354657474 plusOrMinus 1e-12) - rmatn[1, 2] shouldBe (-0.4063240188248455065e-4 plusOrMinus 1e-12) - rmatn[2, 0] shouldBe (-0.3831193642839398128e-5 plusOrMinus 1e-12) - rmatn[2, 1] shouldBe (0.4063236803101479770e-4 plusOrMinus 1e-12) - rmatn[2, 2] shouldBe (0.9999999991671663114 plusOrMinus 1e-12) - } - "eraC2teqx" { - val rbpn = Matrix3D( - 0.9999989440476103608, -0.1332881761240011518e-2, -0.5790767434730085097e-3, - 0.1332858254308954453e-2, 0.9999991109044505944, -0.4097782710401555759e-4, - 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, - ) - - val rpom = Matrix3D( - 0.9999999999999674705, -0.1367174580728847031e-10, 0.2550602379999972723e-6, - 0.1414624947957029721e-10, 0.9999999999982694954, -0.1860359246998866338e-5, - -0.2550602379741215275e-6, 0.1860359247002413923e-5, 0.9999999999982369658, - ) - - val rc2t = eraC2teqx(rbpn, 1.754166138040730516.rad, rpom) - - rc2t[0, 0] shouldBe (-0.1810332128528685730 plusOrMinus 1e-12) - rc2t[0, 1] shouldBe (0.9834769806897685071 plusOrMinus 1e-12) - rc2t[0, 2] shouldBe (0.6555535639982634449e-4 plusOrMinus 1e-12) - - rc2t[1, 0] shouldBe (-0.9834768134095211257 plusOrMinus 1e-12) - rc2t[1, 1] shouldBe (-0.1810332203871023800 plusOrMinus 1e-12) - rc2t[1, 2] shouldBe (0.5749801116126438962e-3 plusOrMinus 1e-12) - - rc2t[2, 0] shouldBe (0.5773474014081539467e-3 plusOrMinus 1e-12) - rc2t[2, 1] shouldBe (0.3961832391768640871e-4 plusOrMinus 1e-12) - rc2t[2, 2] shouldBe (0.9999998325501691969 plusOrMinus 1e-12) - } - "eraTpors" { - val (a, b) = eraTpors((-0.03).rad, 0.07.rad, 1.3.rad, 1.5.rad).shouldNotBeNull() + @Test + fun eraRy() { + val r = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val s = r.rotateY(0.3456789.rad) + + s[0] shouldBe (0.8651847818978159930 plusOrMinus 1e-12) + s[1] shouldBe (1.467194920539316554 plusOrMinus 1e-12) + s[2] shouldBe (0.1875137911274457342 plusOrMinus 1e-12) + s[3] shouldBe (3.0 plusOrMinus 1e-12) + s[4] shouldBe (2.0 plusOrMinus 1e-12) + s[5] shouldBe (3.0 plusOrMinus 1e-12) + s[6] shouldBe (3.500207892850427330 plusOrMinus 1e-12) + s[7] shouldBe (4.779889022262298150 plusOrMinus 1e-12) + s[8] shouldBe (5.381899160903798712 plusOrMinus 1e-12) + + (s == Matrix3D.rotY(0.3456789.rad) * r).shouldBeTrue() + } - a shouldBe (4.004971075806584490 plusOrMinus 1e-13) - b shouldBe (1.565084088476417917 plusOrMinus 1e-13) - } - "eraTpsts" { - val (ra, dec) = eraTpsts((-0.03).rad, 0.07.rad, 2.3.rad, 1.5.rad) + @Test + fun eraRz() { + val r = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val s = r.rotateZ(0.3456789.rad) + + s[0] shouldBe (2.898197754208926769 plusOrMinus 1e-12) + s[1] shouldBe (3.500207892850427330 plusOrMinus 1e-12) + s[2] shouldBe (2.898197754208926769 plusOrMinus 1e-12) + s[3] shouldBe (2.144865911309686813 plusOrMinus 1e-12) + s[4] shouldBe (0.865184781897815993 plusOrMinus 1e-12) + s[5] shouldBe (2.144865911309686813 plusOrMinus 1e-12) + s[6] shouldBe (3.0 plusOrMinus 1e-12) + s[7] shouldBe (4.0 plusOrMinus 1e-12) + s[8] shouldBe (5.0 plusOrMinus 1e-12) + + (s == Matrix3D.rotZ(0.3456789.rad) * r).shouldBeTrue() + } - ra shouldBe (0.7596127167359629775 plusOrMinus 1e-14) - dec shouldBe (1.540864645109263028 plusOrMinus 1e-13) - } - "eraTporv" { - val s = CartesianCoordinate.of(1.3.rad, 1.5.rad, 1.0.au) - val v = doubleArrayOf(s.x, s.y, s.z) + @Test + fun eraRxr() { + val a = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val b = Matrix3D(1.0, 2.0, 2.0, 4.0, 1.0, 1.0, 3.0, 0.0, 1.0) + val c = a * b + + c[0] shouldBe (20.0 plusOrMinus 1e-12) + c[1] shouldBe (7.0 plusOrMinus 1e-12) + c[2] shouldBe (9.0 plusOrMinus 1e-12) + c[3] shouldBe (20.0 plusOrMinus 1e-12) + c[4] shouldBe (8.0 plusOrMinus 1e-12) + c[5] shouldBe (11.0 plusOrMinus 1e-12) + c[6] shouldBe (34.0 plusOrMinus 1e-12) + c[7] shouldBe (10.0 plusOrMinus 1e-12) + c[8] shouldBe (15.0 plusOrMinus 1e-12) + } - val (a, b, c) = eraTporv((-0.03).rad, 0.07.rad, v).shouldNotBeNull() + @Test + fun eraC2s() { + val (theta, phi) = eraC2s(100.0.au, (-50.0).au, 25.0.au) + theta shouldBe (-0.4636476090008061162 plusOrMinus 1e-14) + phi shouldBe (0.2199879773954594463 plusOrMinus 1e-14) + } - a shouldBe (-0.003712211763801968173 plusOrMinus 1e-16) - b shouldBe (-0.004341519956299836813 plusOrMinus 1e-16) - c shouldBe (0.9999836852110587012 plusOrMinus 1e-14) - } - "eraTpstv" { - val s = CartesianCoordinate.of(2.3.rad, 1.5.rad, 1.0.au) - val v = doubleArrayOf(s.x, s.y, s.z) + @Test + fun eraS2c() { + val c = eraS2c(3.0123, -0.999) + c[0] shouldBe (-0.5366267667260523906 plusOrMinus 1e-12) + c[1] shouldBe (0.0697711109765145365 plusOrMinus 1e-12) + c[2] shouldBe (-0.8409302618566214041 plusOrMinus 1e-12) + } - val (a, b, c) = eraTpstv((-0.03).rad, 0.07.rad, v).shouldNotBeNull() + @Test + fun eraP2s() { + val (theta, phi, r) = eraP2s(100.0.au, (-50.0).au, 25.0.au) + theta shouldBe (-0.4636476090008061162 plusOrMinus 1e-12) + phi shouldBe (0.2199879773954594463 plusOrMinus 1e-12) + r shouldBe (114.5643923738960002 plusOrMinus 1e-9) + } - a shouldBe (0.02170030454907376677 plusOrMinus 1e-15) - b shouldBe (0.02060909590535367447 plusOrMinus 1e-15) - c shouldBe (0.999552080658352380 plusOrMinus 1e-14) - } - "eraTpxes" { - val (xi, eta, j) = eraTpxes(1.3.rad, 1.55.rad, 2.3.rad, 1.5.rad) + @Test + fun eraAnpm() { + eraAnpm((-4.0).rad) shouldBe (2.283185307179586477 plusOrMinus 1e-12) + } - xi shouldBe (-0.01753200983236980595 plusOrMinus 1e-15) - eta shouldBe (0.05962940005778712891 plusOrMinus 1e-15) - j shouldBeExactly 0 - } - "eraTpxev" { - val s = CartesianCoordinate.of(1.3.rad, 1.55.rad, 1.0.au) - val v = doubleArrayOf(s.x, s.y, s.z) + @Test + fun eraGc2Gde() { + val (e1, p1, h1) = eraGc2Gde(6378137.0, 1.0 / 298.257223563, 2e6, 3e6, 5.244e6) + e1 shouldBe (0.9827937232473290680 plusOrMinus 1e-14) + p1 shouldBe (0.97160184819075459 plusOrMinus 1e-14) + h1 shouldBe (331.4172461426059892 plusOrMinus 1e-8) + + val (e2, p2, h2) = eraGc2Gde(6378137.0, 1.0 / 298.257222101, 2e6, 3e6, 5.244e6) + e2 shouldBe (0.9827937232473290680 plusOrMinus 1e-14) + p2 shouldBe (0.97160184820607853 plusOrMinus 1e-14) + h2 shouldBe (331.41731754844348 plusOrMinus 1e-8) + + val (e3, p3, h3) = eraGc2Gde(6378135.0, 1.0 / 298.26, 2e6, 3e6, 5.244e6) + e3 shouldBe (0.9827937232473290680 plusOrMinus 1e-14) + p3 shouldBe (0.9716018181101511937 plusOrMinus 1e-14) + h3 shouldBe (333.2770726130318123 plusOrMinus 1e-8) + } - val s0 = CartesianCoordinate.of(2.3.rad, 1.5.rad, 1.0.au) - val v0 = doubleArrayOf(s0.x, s0.y, s0.z) + @Test + fun eraGd2Gc() { + val (x1, y1, z1) = eraGd2Gce(6378137.0, 1.0 / 298.257223563, 3.1.rad, (-0.5).rad, 2500.0) + x1 shouldBe (-5599000.5577049947 plusOrMinus 1e-7) + y1 shouldBe (233011.67223479203 plusOrMinus 1e-7) + z1 shouldBe (-3040909.4706983363 plusOrMinus 1e-7) + + val (x2, y2, z2) = eraGd2Gce(6378137.0, 1.0 / 298.257222101, 3.1.rad, (-0.5).rad, 2500.0) + x2 shouldBe (-5599000.5577260984 plusOrMinus 1e-7) + y2 shouldBe (233011.6722356702949 plusOrMinus 1e-7) + z2 shouldBe (-3040909.4706095476 plusOrMinus 1e-7) + + val (x3, y3, z3) = eraGd2Gce(6378135.0, 1.0 / 298.26, 3.1.rad, (-0.5).rad, 2500.0) + x3 shouldBe (-5598998.7626301490 plusOrMinus 1e-7) + y3 shouldBe (233011.5975297822211 plusOrMinus 1e-7) + z3 shouldBe (-3040908.6861467111 plusOrMinus 1e-7) + } - val (xi, eta, j) = eraTpxev(v, v0) + @Test + fun eraC2ixys() { + val m = eraC2ixys(0.5791308486706011000e-3, 0.4020579816732961219e-4, (-0.1220040848472271978e-7).rad) + m[0, 0] shouldBe (0.9999998323037157138 plusOrMinus 1e-12) + m[0, 1] shouldBe (0.5581984869168499149e-9 plusOrMinus 1e-12) + m[0, 2] shouldBe (-0.5791308491611282180e-3 plusOrMinus 1e-12) + m[1, 0] shouldBe (-0.2384261642670440317e-7 plusOrMinus 1e-12) + m[1, 1] shouldBe (0.9999999991917468964 plusOrMinus 1e-12) + m[1, 2] shouldBe (-0.4020579110169668931e-4 plusOrMinus 1e-12) + m[2, 0] shouldBe (0.5791308486706011000e-3 plusOrMinus 1e-12) + m[2, 1] shouldBe (0.4020579816732961219e-4 plusOrMinus 1e-12) + m[2, 2] shouldBe (0.9999998314954627590 plusOrMinus 1e-12) + } - xi shouldBe (-0.01753200983236980595 plusOrMinus 1e-15) - eta shouldBe (0.05962940005778712891 plusOrMinus 1e-15) - j shouldBeExactly 0 - } - "eraPb06" { - val (zeta, z, theta) = eraPb06(2400000.5, 50123.9999) + @Test + fun eraPom00() { + val m = eraPom00(2.55060238e-7.rad, 1.860359247e-6.rad, (-0.1367174580728891460e-10).rad) + m[0, 0] shouldBe (0.9999999999999674721 plusOrMinus 1e-12) + m[0, 1] shouldBe (-0.1367174580728846989e-10 plusOrMinus 1e-12) + m[0, 2] shouldBe (0.2550602379999972345e-6 plusOrMinus 1e-12) + m[1, 0] shouldBe (0.1414624947957029801e-10 plusOrMinus 1e-12) + m[1, 1] shouldBe (0.9999999999982695317 plusOrMinus 1e-12) + m[1, 2] shouldBe (-0.1860359246998866389e-5 plusOrMinus 1e-12) + m[2, 0] shouldBe (-0.2550602379741215021e-6 plusOrMinus 1e-12) + m[2, 1] shouldBe (0.1860359247002414021e-5 plusOrMinus 1e-12) + m[2, 2] shouldBe (0.9999999999982370039 plusOrMinus 1e-12) + } - zeta shouldBe (-0.5092634016326478238e-3 plusOrMinus 1e-12) - z shouldBe (-0.3602772060566044413e-3 plusOrMinus 1e-12) - theta shouldBe (-0.3779735537167811177e-3 plusOrMinus 1e-12) - } - "eraJd2Cal" { - val (y, m, d, f) = eraJd2Cal(2400000.5, 50123.9999) - y shouldBeExactly 1996 - m shouldBeExactly 2 - d shouldBeExactly 10 - f shouldBe (0.9999 plusOrMinus 1e-7) - } - "eraCal2Jd" { - eraCal2Jd(2003, 6, 1) shouldBeExactly 52791.0 - } - "eraDat" { - eraDat(2003, 6, 1, 0.0) shouldBeExactly 32.0 - eraDat(2008, 1, 17, 0.0) shouldBeExactly 33.0 - eraDat(2017, 9, 1, 0.0) shouldBeExactly 37.0 - } - "eraUt1Utc" { - val (u1, u2) = eraUt1Utc(2453750.5, 0.892104561, 0.3341) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8921006941018518519 plusOrMinus 1e-12) - } - "eraUtcTai" { - val (u1, u2) = eraUtcTai(2453750.5, 0.892100694) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8924826384444444444 plusOrMinus 1e-12) - } - "eraTaiUt1" { - val (u1, u2) = eraTaiUt1(2453750.5, 0.892482639, -32.6659) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8921045614537037037 plusOrMinus 1e-12) - } - "eraUtcUt1" { - val (u1, u2) = eraUtcUt1(2453750.5, 0.892100694, 0.3341) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8921045608981481481 plusOrMinus 1e-12) - } - "eraTaiUtc" { - val (u1, u2) = eraTaiUtc(2453750.5, 0.892482639) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8921006945555555556 plusOrMinus 1e-12) - } - "eraTaiTt" { - val (u1, u2) = eraTaiTt(2453750.5, 0.892482639) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.892855139 plusOrMinus 1e-12) - } - "eraTtTai" { - val (u1, u2) = eraTtTai(2453750.5, 0.892482639) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.892110139 plusOrMinus 1e-12) - } - "eraTtTdb" { - val (u1, u2) = eraTtTdb(2453750.5, 0.892855139, -0.000201) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8928551366736111111 plusOrMinus 1e-12) - } - "eraTdbTt" { - val (u1, u2) = eraTdbTt(2453750.5, 0.892855137, -0.000201) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8928551393263888889 plusOrMinus 1e-12) - } - "eraUt1Tai" { - val (u1, u2) = eraUt1Tai(2453750.5, 0.892104561, -32.6659) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8924826385462962963 plusOrMinus 1e-12) - } - "eraTtUt1" { - val (u1, u2) = eraTtUt1(2453750.5, 0.892855139, 64.8499) - u1 shouldBeExactly 2453750.5 - u2 shouldBe (0.8921045614537037037 plusOrMinus 1e-12) - } - "eraBp00" { - val (rb, rp, rbp) = eraBp00(2400000.5, 50123.9999) - - rb[0, 0] shouldBe (0.9999999999999942498 plusOrMinus 1e-12) - rb[0, 1] shouldBe (-0.7078279744199196626e-7 plusOrMinus 1e-16) - rb[0, 2] shouldBe (0.8056217146976134152e-7 plusOrMinus 1e-16) - rb[1, 0] shouldBe (0.7078279477857337206e-7 plusOrMinus 1e-16) - rb[1, 1] shouldBe (0.9999999999999969484 plusOrMinus 1e-12) - rb[1, 2] shouldBe (0.3306041454222136517e-7 plusOrMinus 1e-16) - rb[2, 0] shouldBe (-0.8056217380986972157e-7 plusOrMinus 1e-16) - rb[2, 1] shouldBe (-0.3306040883980552500e-7 plusOrMinus 1e-16) - rb[2, 2] shouldBe (0.9999999999999962084 plusOrMinus 1e-12) - rp[0, 0] shouldBe (0.9999995504864048241 plusOrMinus 1e-12) - rp[0, 1] shouldBe (0.8696113836207084411e-3 plusOrMinus 1e-14) - rp[0, 2] shouldBe (0.3778928813389333402e-3 plusOrMinus 1e-14) - rp[1, 0] shouldBe (-0.8696113818227265968e-3 plusOrMinus 1e-14) - rp[1, 1] shouldBe (0.9999996218879365258 plusOrMinus 1e-12) - rp[1, 2] shouldBe (-0.1690679263009242066e-6 plusOrMinus 1e-14) - rp[2, 0] shouldBe (-0.3778928854764695214e-3 plusOrMinus 1e-14) - rp[2, 1] shouldBe (-0.1595521004195286491e-6 plusOrMinus 1e-14) - rp[2, 2] shouldBe (0.9999999285984682756 plusOrMinus 1e-12) - rbp[0, 0] shouldBe (0.9999995505175087260 plusOrMinus 1e-12) - rbp[0, 1] shouldBe (0.8695405883617884705e-3 plusOrMinus 1e-14) - rbp[0, 2] shouldBe (0.3779734722239007105e-3 plusOrMinus 1e-14) - rbp[1, 0] shouldBe (-0.8695405990410863719e-3 plusOrMinus 1e-14) - rbp[1, 1] shouldBe (0.9999996219494925900 plusOrMinus 1e-12) - rbp[1, 2] shouldBe (-0.1360775820404982209e-6 plusOrMinus 1e-14) - rbp[2, 0] shouldBe (-0.3779734476558184991e-3 plusOrMinus 1e-14) - rbp[2, 1] shouldBe (-0.1925857585832024058e-6 plusOrMinus 1e-14) - rbp[2, 2] shouldBe (0.9999999285680153377 plusOrMinus 1e-12) - } - "eraPn00" { - val (_, _, epsa, rb, rp, rbp, rn, rbpn) = eraPn00(2400000.5, 53736.0, -0.9632552291149335877e-5, 0.4063197106621141414e-4) + @Test + fun eraApcs() { + val astro = eraApcs( + 2456384.5, 0.970031644, + Vector3D(-1836024.09, 1056607.7, -5998795.26), + Vector3D(-77.0361767, -133.310856, 0.0971855934), + Vector3D(-0.974170438, -0.211520082, -0.0917583024), + Vector3D(0.00364365824, -0.0154287319, -0.00668922024), + Vector3D(-0.973458265, -0.209215307, -0.0906996477), + ) + + astro.pmt shouldBe (13.25248468622587269 plusOrMinus 1e-11) + astro.eb.x shouldBe (-0.9741827110629881886 plusOrMinus 1e-12) + astro.eb.y shouldBe (-0.2115130190136415986 plusOrMinus 1e-12) + astro.eb.z shouldBe (-0.09179840186954412099 plusOrMinus 1e-12) + astro.eh.x shouldBe (-0.9736425571689454706 plusOrMinus 1e-12) + astro.eh.y shouldBe (-0.2092452125850435930 plusOrMinus 1e-12) + astro.eh.z shouldBe (-0.09075578152248299218 plusOrMinus 1e-12) + astro.em shouldBe (0.9998233241709796859 plusOrMinus 1e-12) + astro.v.x shouldBe (0.2078704993282685510e-4 plusOrMinus 1e-16) + astro.v.y shouldBe (-0.8955360106989405683e-4 plusOrMinus 1e-16) + astro.v.z shouldBe (-0.3863338994289409097e-4 plusOrMinus 1e-16) + astro.bm1 shouldBe (0.9999999950277561237 plusOrMinus 1e-12) + } - epsa shouldBe (0.4090791789404229916 plusOrMinus 1e-12) + @Test + fun eraApco() { + val astro = eraApco( + 2456384.5, 0.970031644, + Vector3D(-0.974170438, -0.211520082, -0.0917583024), + Vector3D(0.00364365824, -0.0154287319, -0.00668922024), + Vector3D(-0.973458265, -0.209215307, -0.0906996477), + 0.0013122272, -2.92808623e-5, 3.05749468e-8.rad, + 3.14540971.rad, (-0.527800806).rad, (-1.2345856).rad, + 2738.0, + 2.47230737e-7.rad, 1.82640464e-6.rad, (-3.01974337e-11).rad, + 0.000201418779.rad, (-2.36140831e-7).rad, + ) + + astro.pmt shouldBe (13.25248468622587269 plusOrMinus 1e-11) + astro.eb.x shouldBe (-0.9741827110630322720 plusOrMinus 1e-12) + astro.eb.y shouldBe (-0.2115130190135344832 plusOrMinus 1e-12) + astro.eb.z shouldBe (-0.09179840186949532298 plusOrMinus 1e-12) + astro.eh.x shouldBe (-0.9736425571689739035 plusOrMinus 1e-12) + astro.eh.y shouldBe (-0.2092452125849330936 plusOrMinus 1e-12) + astro.eh.z shouldBe (-0.09075578152243272599 plusOrMinus 1e-12) + astro.em shouldBe (0.9998233241709957653 plusOrMinus 1e-12) + astro.v.x shouldBe (0.2078704992916728762e-4 plusOrMinus 1e-16) + astro.v.y shouldBe (-0.8955360107151952319e-4 plusOrMinus 1e-16) + astro.v.z shouldBe (-0.3863338994288951082e-4 plusOrMinus 1e-16) + astro.bm1 shouldBe (0.9999999950277561236 plusOrMinus 1e-12) + astro.bpn[0, 0] shouldBe (0.9999991390295159156 plusOrMinus 1e-12) + astro.bpn[1, 0] shouldBe (0.4978650072505016932e-7 plusOrMinus 1e-12) + astro.bpn[2, 0] shouldBe (0.1312227200000000000e-2 plusOrMinus 1e-12) + astro.bpn[0, 1] shouldBe (-0.1136336653771609630e-7 plusOrMinus 1e-12) + astro.bpn[1, 1] shouldBe (0.9999999995713154868 plusOrMinus 1e-12) + astro.bpn[2, 1] shouldBe (-0.2928086230000000000e-4 plusOrMinus 1e-12) + astro.bpn[0, 2] shouldBe (-0.1312227200895260194e-2 plusOrMinus 1e-12) + astro.bpn[1, 2] shouldBe (0.2928082217872315680e-4 plusOrMinus 1e-12) + astro.bpn[2, 2] shouldBe (0.9999991386008323373 plusOrMinus 1e-12) + astro.along shouldBe (-0.5278008060295995734 plusOrMinus 1e-12) + astro.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) + astro.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) + astro.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) + astro.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) + astro.eral shouldBe (2.617608903970400427 plusOrMinus 1e-12) + astro.refa shouldBe (0.2014187790000000000e-3 plusOrMinus 1e-17) + astro.refb shouldBe (-0.2361408310000000000e-6 plusOrMinus 1e-18) + astro.diurab shouldBeExactly 0.0 + } - rb[0, 0] shouldBe (0.9999999999999942498 plusOrMinus 1e-12) - rb[0, 1] shouldBe (-0.7078279744199196626e-7 plusOrMinus 1e-18) - rb[0, 2] shouldBe (0.8056217146976134152e-7 plusOrMinus 1e-18) + @Test + fun eraSp00() { + eraSp00(2400000.5, 52541.0) shouldBe (-0.6216698469981019309e-11 plusOrMinus 1e-12) + } - rb[1, 0] shouldBe (0.7078279477857337206e-7 plusOrMinus 1e-18) - rb[1, 1] shouldBe (0.9999999999999969484 plusOrMinus 1e-12) - rb[1, 2] shouldBe (0.3306041454222136517e-7 plusOrMinus 1e-18) + @Test + fun eraObl06() { + eraObl06(2400000.5, 54388.0) shouldBe (0.4090749229387258204 plusOrMinus 1e-14) + } - rb[2, 0] shouldBe (-0.8056217380986972157e-7 plusOrMinus 1e-18) - rb[2, 1] shouldBe (-0.3306040883980552500e-7 plusOrMinus 1e-18) - rb[2, 2] shouldBe (0.9999999999999962084 plusOrMinus 1e-12) + @Test + fun eraPfw06() { + val (gamb, phib, psib, epsa) = eraPfw06(2400000.5, 50123.9999) + gamb shouldBe (-0.2243387670997995690e-5 plusOrMinus 1e-16) + phib shouldBe (0.4091014602391312808 plusOrMinus 1e-12) + psib shouldBe (-0.9501954178013031895e-3 plusOrMinus 1e-14) + epsa shouldBe (0.4091014316587367491 plusOrMinus 1e-12) + } - rp[0, 0] shouldBe (0.9999989300532289018 plusOrMinus 1e-12) - rp[0, 1] shouldBe (-0.1341647226791824349e-2 plusOrMinus 1e-14) - rp[0, 2] shouldBe (-0.5829880927190296547e-3 plusOrMinus 1e-14) + @Test + fun eraFal03() { + eraFal03(0.80) shouldBe (5.132369751108684150 plusOrMinus 1e-12) + } - rp[1, 0] shouldBe (0.1341647231069759008e-2 plusOrMinus 1e-14) - rp[1, 1] shouldBe (0.9999990999908750433 plusOrMinus 1e-12) - rp[1, 2] shouldBe (-0.3837444441583715468e-6 plusOrMinus 1e-14) + @Test + fun eraFaf03() { + eraFaf03(0.80) shouldBe (0.2597711366745499518 plusOrMinus 1e-12) + } - rp[2, 0] shouldBe (0.5829880828740957684e-3 plusOrMinus 1e-14) - rp[2, 1] shouldBe (-0.3984203267708834759e-6 plusOrMinus 1e-14) - rp[2, 2] shouldBe (0.9999998300623538046 plusOrMinus 1e-12) + @Test + fun eraFaom03() { + eraFaom03(0.80) shouldBe (TAU - 5.973618440951302183 plusOrMinus 1e-12) + } - rbp[0, 0] shouldBe (0.9999989300052243993 plusOrMinus 1e-12) - rbp[0, 1] shouldBe (-0.1341717990239703727e-2 plusOrMinus 1e-14) - rbp[0, 2] shouldBe (-0.5829075749891684053e-3 plusOrMinus 1e-14) + @Test + fun eraFapa03() { + eraFapa03(0.80) shouldBe (0.1950884762240000000e-1 plusOrMinus 1e-12) + } - rbp[1, 0] shouldBe (0.1341718013831739992e-2 plusOrMinus 1e-14) - rbp[1, 1] shouldBe (0.9999990998959191343 plusOrMinus 1e-12) - rbp[1, 2] shouldBe (-0.3505759733565421170e-6 plusOrMinus 1e-14) + @Test + fun eraFame03() { + eraFame03(0.80) shouldBe (5.417338184297289661 plusOrMinus 1e-12) + } - rbp[2, 0] shouldBe (0.5829075206857717883e-3 plusOrMinus 1e-14) - rbp[2, 1] shouldBe (-0.4315219955198608970e-6 plusOrMinus 1e-14) - rbp[2, 2] shouldBe (0.9999998301093036269 plusOrMinus 1e-12) + @Test + fun eraFave03() { + eraFave03(0.80) shouldBe (3.424900460533758000 plusOrMinus 1e-12) + } - rn[0, 0] shouldBe (0.9999999999536069682 plusOrMinus 1e-12) - rn[0, 1] shouldBe (0.8837746144872140812e-5 plusOrMinus 1e-16) - rn[0, 2] shouldBe (0.3831488838252590008e-5 plusOrMinus 1e-16) + @Test + fun eraFae03() { + eraFae03(0.80) shouldBe (1.744713738913081846 plusOrMinus 1e-12) + } - rn[1, 0] shouldBe (-0.8837590456633197506e-5 plusOrMinus 1e-16) - rn[1, 1] shouldBe (0.9999999991354692733 plusOrMinus 1e-12) - rn[1, 2] shouldBe (-0.4063198798559573702e-4 plusOrMinus 1e-16) + @Test + fun eraFama03() { + eraFama03(0.80) shouldBe (3.275506840277781492 plusOrMinus 1e-12) + } - rn[2, 0] shouldBe (-0.3831847930135328368e-5 plusOrMinus 1e-16) - rn[2, 1] shouldBe (0.4063195412258150427e-4 plusOrMinus 1e-16) - rn[2, 2] shouldBe (0.9999999991671806225 plusOrMinus 1e-12) + @Test + fun eraFaju03() { + eraFaju03(0.80) shouldBe (5.275711665202481138 plusOrMinus 1e-12) + } - rbpn[0, 0] shouldBe (0.9999989440499982806 plusOrMinus 1e-12) - rbpn[0, 1] shouldBe (-0.1332880253640848301e-2 plusOrMinus 1e-14) - rbpn[0, 2] shouldBe (-0.5790760898731087295e-3 plusOrMinus 1e-14) + @Test + fun eraFasa03() { + eraFasa03(0.80) shouldBe (5.371574539440827046 plusOrMinus 1e-12) + } - rbpn[1, 0] shouldBe (0.1332856746979948745e-2 plusOrMinus 1e-14) - rbpn[1, 1] shouldBe (0.9999991109064768883 plusOrMinus 1e-12) - rbpn[1, 2] shouldBe (-0.4097740555723063806e-4 plusOrMinus 1e-14) + @Test + fun eraFaur03() { + eraFaur03(0.80) shouldBe (5.180636450180413523 plusOrMinus 1e-12) + } - rbpn[2, 0] shouldBe (0.5791301929950205000e-3 plusOrMinus 1e-14) - rbpn[2, 1] shouldBe (0.4020553681373702931e-4 plusOrMinus 1e-14) - rbpn[2, 2] shouldBe (0.9999998314958529887 plusOrMinus 1e-12) - } - "eraC2tpe" { - val rc2t = - eraC2tpe(2400000.5, 53736.0, 2400000.5, 53736.0, -0.9630909107115582393e-5, 0.4090789763356509900, 2.55060238e-7, 1.860359247e-6) + @Test + fun eraFw2m() { + val m = eraFw2m((-0.2243387670997992368e-5).rad, 0.4091014602391312982.rad, (-0.9501954178013015092e-3).rad, 0.4091014316587367472.rad) + m[0, 0] shouldBe (0.9999995505176007047 plusOrMinus 1e-12) + m[0, 1] shouldBe (0.8695404617348192957e-3 plusOrMinus 1e-12) + m[0, 2] shouldBe (0.3779735201865582571e-3 plusOrMinus 1e-12) + m[1, 0] shouldBe (-0.8695404723772016038e-3 plusOrMinus 1e-12) + m[1, 1] shouldBe (0.9999996219496027161 plusOrMinus 1e-12) + m[1, 2] shouldBe (-0.1361752496887100026e-6 plusOrMinus 1e-12) + m[2, 0] shouldBe (-0.3779734957034082790e-3 plusOrMinus 1e-12) + m[2, 1] shouldBe (-0.1924880848087615651e-6 plusOrMinus 1e-12) + m[2, 2] shouldBe (0.9999999285679971958 plusOrMinus 1e-12) + } - rc2t[0, 0] shouldBe (-0.1813677995763029394 plusOrMinus 1e-12) - rc2t[0, 1] shouldBe (0.9023482206891683275 plusOrMinus 1e-12) - rc2t[0, 2] shouldBe (-0.3909902938641085751 plusOrMinus 1e-12) + @Test + fun era00() { + era00(2400000.0 + 54388.0, 0.5) shouldBe (0.4022837240028158102 plusOrMinus 1e-12) + } - rc2t[1, 0] shouldBe (-0.9834147641476804807 plusOrMinus 1e-12) - rc2t[1, 1] shouldBe (-0.1659883635434995121 plusOrMinus 1e-12) - rc2t[1, 2] shouldBe (0.7309763898042819705e-1 plusOrMinus 1e-12) + @Test + fun eraRefco() { + val (refa, refb) = eraRefco(800.0, 10.0, 0.9, 0.4) + refa shouldBe (0.2264949956241415009e-3 plusOrMinus 1e-15) + refb shouldBe (-0.2598658261729343970e-6 plusOrMinus 1e-18) + } - rc2t[2, 0] shouldBe (0.1059685430673215247e-2 plusOrMinus 1e-12) - rc2t[2, 1] shouldBe (0.3977631855605078674 plusOrMinus 1e-12) - rc2t[2, 2] shouldBe (0.9174875068792735362 plusOrMinus 1e-12) - } - "eraS00" { - val s = eraS00(2400000.5, 53736.0, 0.5791308486706011000e-3, 0.4020579816732961219e-4) - s shouldBe (-0.1220036263270905693e-7 plusOrMinus 1e-18) - } - "eraS00b" { - val s = eraS00b(2400000.5, 52541.0) - s shouldBe (-0.1340695782951026584e-7 plusOrMinus 1e-18) - } - "eraS00a" { - val s = eraS00a(2400000.5, 52541.0) - s shouldBe (-0.1340684448919163584e-7 plusOrMinus 1e-18) - } - "eraApco13" { - val (astrom, eo) = eraApco13( - 2456384.5, 0.969254051, 0.1550675, - -0.527800806, -1.2345856, 2738.0, - 2.47230737e-7, 1.82640464e-6, - 731.0, 12.8, 0.59, 0.55 - ) - - astrom.pmt shouldBe (13.25248468622475727 plusOrMinus 1e-11) - astrom.eb.x shouldBe (-0.9741827107320875162 plusOrMinus 1e-12) - astrom.eb.y shouldBe (-0.2115130190489716682 plusOrMinus 1e-12) - astrom.eb.z shouldBe (-0.09179840189496755339 plusOrMinus 1e-12) - astrom.eh.x shouldBe (-0.9736425572586935247 plusOrMinus 1e-12) - astrom.eh.y shouldBe (-0.2092452121603336166 plusOrMinus 1e-12) - astrom.eh.z shouldBe (-0.09075578153885665295 plusOrMinus 1e-12) - astrom.em shouldBe (0.9998233240913898141 plusOrMinus 1e-12) - astrom.v.x shouldBe (0.2078704994520489246e-4 plusOrMinus 1e-16) - astrom.v.y shouldBe (-0.8955360133238868938e-4 plusOrMinus 1e-16) - astrom.v.z shouldBe (-0.3863338993055887398e-4 plusOrMinus 1e-16) - astrom.bm1 shouldBe (0.9999999950277561004 plusOrMinus 1e-12) - astrom.bpn[0, 0] shouldBe (0.9999991390295147999 plusOrMinus 1e-12) - astrom.bpn[1, 0] shouldBe (0.4978650075315529277e-7 plusOrMinus 1e-12) - astrom.bpn[2, 0] shouldBe (0.001312227200850293372 plusOrMinus 1e-12) - astrom.bpn[0, 1] shouldBe (-0.1136336652812486604e-7 plusOrMinus 1e-12) - astrom.bpn[1, 1] shouldBe (0.9999999995713154865 plusOrMinus 1e-12) - astrom.bpn[2, 1] shouldBe (-0.2928086230975367296e-4 plusOrMinus 1e-12) - astrom.bpn[0, 2] shouldBe (-0.001312227201745553566 plusOrMinus 1e-12) - astrom.bpn[1, 2] shouldBe (0.2928082218847679162e-4 plusOrMinus 1e-12) - astrom.bpn[2, 2] shouldBe (0.9999991386008312212 plusOrMinus 1e-12) - astrom.along shouldBe (-0.5278008060295995733 plusOrMinus 1e-12) - astrom.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) - astrom.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) - astrom.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) - astrom.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) - astrom.diurab shouldBeExactly 0.0 - astrom.eral shouldBe (2.617608909189664000 plusOrMinus 1e-12) - astrom.refa shouldBe (0.2014187785940396921e-3 plusOrMinus 1e-15) - astrom.refb shouldBe (-0.2361408314943696227e-6 plusOrMinus 1e-18) - eo shouldBe (-0.003020548354802412839 plusOrMinus 1e-14) - } - "eraPmpx" { - val pco = eraPmpx(1.234, 0.789, 1e-5, -2e-5, 1e-2.arcsec, 10.0.kms, 8.75, Vector3D(0.9, 0.4, 0.1)) - pco[0] shouldBe (0.2328137623960308438 plusOrMinus 1e-12) - pco[1] shouldBe (0.6651097085397855328 plusOrMinus 1e-12) - pco[2] shouldBe (0.7095257765896359837 plusOrMinus 1e-12) - } - "eraAtciq" { - val (astrom) = eraApci13(2456165.5, 0.401182685) - val (ri, di) = eraAtciq(2.71, 0.174, 1e-5, 5e-6, 0.1.arcsec, 55.0.kms, astrom) - ri shouldBe (2.710121572968696744 plusOrMinus 1e-12) - di shouldBe (0.1729371367219539137 plusOrMinus 1e-12) - } - "eraAtciqz" { - val (astrom) = eraApci13(2456165.5, 0.401182685) - val (ri, di) = eraAtciqz(2.71, 0.174, astrom) - ri shouldBe (2.709994899247256984 plusOrMinus 1e-12) - di shouldBe (0.1728740720984931891 plusOrMinus 1e-12) - } - "eraAtci13" { - val (ri, di, eo) = eraAtci13(2.71, 0.174, 1e-5, 5e-6, 0.1.arcsec, 55.0.kms, 2456165.5, 0.401182685) - ri shouldBe (2.710121572968696744 plusOrMinus 1e-12) - di shouldBe (0.1729371367219539137 plusOrMinus 1e-12) - eo shouldBe (-0.002900618712657375647 plusOrMinus 1e-14) - } - "eraApci13" { - val (astrom, eo) = eraApci13(2456165.5, 0.401182685) - - astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) - astrom.eb[0] shouldBe (0.9013108747340644755 plusOrMinus 1e-12) - astrom.eb[1] shouldBe (-0.4174026640406119957 plusOrMinus 1e-12) - astrom.eb[2] shouldBe (-0.1809822877867817771 plusOrMinus 1e-12) - astrom.eh[0] shouldBe (0.8940025429255499549 plusOrMinus 1e-12) - astrom.eh[1] shouldBe (-0.4110930268331896318 plusOrMinus 1e-12) - astrom.eh[2] shouldBe (-0.1782189006019749850 plusOrMinus 1e-12) - astrom.em shouldBe (1.010465295964664178 plusOrMinus 1e-12) - astrom.v[0] shouldBe (0.4289638912941341125e-4 plusOrMinus 1e-16) - astrom.v[1] shouldBe (0.8115034032405042132e-4 plusOrMinus 1e-16) - astrom.v[2] shouldBe (0.3517555135536470279e-4 plusOrMinus 1e-16) - astrom.bm1 shouldBe (0.9999999951686013142 plusOrMinus 1e-12) - astrom.bpn[0, 0] shouldBe (0.9999992060376761710 plusOrMinus 1e-12) - astrom.bpn[1, 0] shouldBe (0.4124244860106037157e-7 plusOrMinus 1e-12) - astrom.bpn[2, 0] shouldBe (0.1260128571051709670e-2 plusOrMinus 1e-12) - astrom.bpn[0, 1] shouldBe (-0.1282291987222130690e-7 plusOrMinus 1e-12) - astrom.bpn[1, 1] shouldBe (0.9999999997456835325 plusOrMinus 1e-12) - astrom.bpn[2, 1] shouldBe (-0.2255288829420524935e-4 plusOrMinus 1e-12) - astrom.bpn[0, 2] shouldBe (-0.1260128571661374559e-2 plusOrMinus 1e-12) - astrom.bpn[1, 2] shouldBe (0.2255285422953395494e-4 plusOrMinus 1e-12) - astrom.bpn[2, 2] shouldBe (0.9999992057833604343 plusOrMinus 1e-12) - eo shouldBe (-0.2900618712657375647e-2 plusOrMinus 1e-12) - } - "eraLdsun" { - val p = Vector3D(-0.763276255, -0.608633767, -0.216735543) - val e = Vector3D(-0.973644023, -0.20925523, -0.0907169552) - val p1 = eraLdsun(p, e, 0.999809214) - p1[0] shouldBe (-0.7632762580731413169 plusOrMinus 1e-12) - p1[1] shouldBe (-0.6086337635262647900 plusOrMinus 1e-12) - p1[2] shouldBe (-0.2167355419322321302 plusOrMinus 1e-12) - } - "eraLd" { - val p = Vector3D(-0.763276255, -0.608633767, -0.216735543) - val q = Vector3D(-0.763276255, -0.608633767, -0.216735543) - val e = Vector3D(0.76700421, 0.605629598, 0.211937094) - val p1 = eraLd(0.00028574, p, q, e, 8.91276983, 3e-10) - p1[0] shouldBe (-0.7632762548968159627 plusOrMinus 1e-12) - p1[1] shouldBe (-0.6086337670823762701 plusOrMinus 1e-12) - p1[2] shouldBe (-0.2167355431320546947 plusOrMinus 1e-12) - } - "eraApci" { - val ebp = Vector3D(0.901310875, -0.417402664, -0.180982288) - val ebv = Vector3D(0.00742727954, 0.0140507459, 0.00609045792) - val ehp = Vector3D(0.903358544, -0.415395237, -0.180084014) - val astrom = eraApci(2456165.5, 0.401182685, ebp, ebv, ehp, 0.0013122272, -2.92808623e-5, 3.05749468e-8) - - astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) - astrom.eb[0] shouldBe (0.901310875 plusOrMinus 1e-12) - astrom.eb[1] shouldBe (-0.417402664 plusOrMinus 1e-12) - astrom.eb[2] shouldBe (-0.180982288 plusOrMinus 1e-12) - astrom.eh[0] shouldBe (0.8940025429324143045 plusOrMinus 1e-12) - astrom.eh[1] shouldBe (-0.4110930268679817955 plusOrMinus 1e-12) - astrom.eh[2] shouldBe (-0.1782189004872870264 plusOrMinus 1e-12) - astrom.em shouldBe (1.010465295811013146 plusOrMinus 1e-12) - astrom.v[0] shouldBe (0.4289638913597693554e-4 plusOrMinus 1e-16) - astrom.v[1] shouldBe (0.8115034051581320575e-4 plusOrMinus 1e-16) - astrom.v[2] shouldBe (0.3517555136380563427e-4 plusOrMinus 1e-16) - astrom.bm1 shouldBe (0.9999999951686012981 plusOrMinus 1e-12) - astrom.bpn[0, 0] shouldBe (0.9999991390295159156 plusOrMinus 1e-12) - astrom.bpn[1, 0] shouldBe (0.4978650072505016932e-7 plusOrMinus 1e-12) - astrom.bpn[2, 0] shouldBe (0.1312227200000000000e-2 plusOrMinus 1e-12) - astrom.bpn[0, 1] shouldBe (-0.1136336653771609630e-7 plusOrMinus 1e-12) - astrom.bpn[1, 1] shouldBe (0.9999999995713154868 plusOrMinus 1e-12) - astrom.bpn[2, 1] shouldBe (-0.2928086230000000000e-4 plusOrMinus 1e-12) - astrom.bpn[0, 2] shouldBe (-0.1312227200895260194e-2 plusOrMinus 1e-12) - astrom.bpn[1, 2] shouldBe (0.2928082217872315680e-4 plusOrMinus 1e-12) - astrom.bpn[2, 2] shouldBe (0.9999991386008323373 plusOrMinus 1e-12) - } - "eraApcs13" { - val p = Vector3D(-6241497.16, 401346.896, -1251136.04) - val v = Vector3D(-29.264597, -455.021831, 0.0266151194) - val astrom = eraApcs13(2456165.5, 0.401182685, p, v) - - astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) - astrom.eb[0] shouldBe (0.9012691529025250644 plusOrMinus 1e-12) - astrom.eb[1] shouldBe (-0.4173999812023194317 plusOrMinus 1e-12) - astrom.eb[2] shouldBe (-0.1809906511146429670 plusOrMinus 1e-12) - astrom.eh[0] shouldBe (0.8939939101760130792 plusOrMinus 1e-12) - astrom.eh[1] shouldBe (-0.4111053891734021478 plusOrMinus 1e-12) - astrom.eh[2] shouldBe (-0.1782336880636997374 plusOrMinus 1e-12) - astrom.em shouldBe (1.010428384373491095 plusOrMinus 1e-12) - astrom.v[0] shouldBe (0.4279877294121697570e-4 plusOrMinus 1e-16) - astrom.v[1] shouldBe (0.7963255087052120678e-4 plusOrMinus 1e-16) - astrom.v[2] shouldBe (0.3517564013384691531e-4 plusOrMinus 1e-16) - astrom.bm1 shouldBe (0.9999999952947980978 plusOrMinus 1e-12) - astrom.bpn shouldBe Matrix3D.IDENTITY - } - "eraAtioq" { - val astrom = - eraApio13(2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55) - val (aob, zob, hob, dob, rob) = eraAtioq(2.710121572969038991, 0.1729371367218230438, astrom) - - aob shouldBe (0.9233952224895122499e-1 plusOrMinus 1e-12) - zob shouldBe (1.407758704513549991 plusOrMinus 1e-12) - hob shouldBe (-0.9247619879881698140e-1 plusOrMinus 1e-12) - dob shouldBe (0.1717653435756234676 plusOrMinus 1e-12) - rob shouldBe (2.710085107988480746 plusOrMinus 1e-12) - } - "eraApio13" { - val astrom = - eraApio13(2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55) - - astrom.along shouldBe (-0.5278008060295995733 plusOrMinus 1e-12) - astrom.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) - astrom.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) - astrom.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) - astrom.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) - astrom.diurab shouldBe (0.5135843661699913529e-6 plusOrMinus 1e-12) - astrom.eral shouldBe (2.617608909189664000 plusOrMinus 1e-12) - astrom.refa shouldBe (0.2014187785940396921e-3 plusOrMinus 1e-15) - astrom.refb shouldBe (-0.2361408314943696227e-6 plusOrMinus 1e-18) - } - "eraApio" { - val astrom = - eraApio(-3.01974337e-11, 3.14540971, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 0.000201418779, -2.36140831e-7) - - astrom.along shouldBe (-0.5278008060295995734 plusOrMinus 1e-12) - astrom.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) - astrom.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) - astrom.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) - astrom.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) - astrom.diurab shouldBe (0.5135843661699913529e-6 plusOrMinus 1e-12) - astrom.eral shouldBe (2.617608903970400427 plusOrMinus 1e-12) - astrom.refa shouldBe (0.2014187790000000000e-3 plusOrMinus 1e-15) - astrom.refb shouldBe (-0.2361408310000000000e-6 plusOrMinus 1e-18) - } - "eraAtco13" { - val (b, eo) = eraAtco13( - 2.71, 0.174, 1e-5, 5e-6, 0.1.arcsec, 55.0.kms, 2456384.5, 0.969254051, - 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, - 0.59, 0.55 - ) - - val (aob, zob, hob, dob, rob) = b - - aob shouldBe (0.9251774485485515207e-1 plusOrMinus 1e-12) - zob shouldBe (1.407661405256499357 plusOrMinus 1e-12) - hob shouldBe (-0.9265154431529724692e-1 plusOrMinus 1e-12) - dob shouldBe (0.1716626560072526200 plusOrMinus 1e-12) - rob shouldBe (2.710260453504961012 plusOrMinus 1e-12) - eo shouldBe (-0.003020548354802412839 plusOrMinus 1e-14) - } - "eraAticq" { - val (astrom) = eraApci13(2456165.5, 0.401182685) - val (ri, di) = eraAticq(2.710121572969038991, 0.1729371367218230438, astrom) - ri shouldBe (2.710126504531716819 plusOrMinus 1e-12) - di shouldBe (0.1740632537627034482 plusOrMinus 1e-12) - } - "eraAtic13" { - val (rc, dc, eo) = eraAtic13(2.710121572969038991, 0.1729371367218230438, 2456165.5, 0.401182685) + @Test + fun eraApcg() { + val astrom = eraApcg( + 2456165.5, 0.401182685, + Vector3D(0.901310875, -0.417402664, -0.180982288), + Vector3D(0.00742727954, 0.0140507459, 0.00609045792), + Vector3D(0.903358544, -0.415395237, -0.180084014), + ) + + astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) + astrom.eb.x shouldBe (0.901310875 plusOrMinus 1e-12) + astrom.eb.y shouldBe (-0.417402664 plusOrMinus 1e-12) + astrom.eb.z shouldBe (-0.180982288 plusOrMinus 1e-12) + astrom.eh.x shouldBe (0.8940025429324143045 plusOrMinus 1e-12) + astrom.eh.y shouldBe (-0.4110930268679817955 plusOrMinus 1e-12) + astrom.eh.z shouldBe (-0.1782189004872870264 plusOrMinus 1e-12) + astrom.em shouldBe (1.010465295811013146 plusOrMinus 1e-12) + astrom.v.x shouldBe (0.4289638913597693554e-4 plusOrMinus 1e-16) + astrom.v.y shouldBe (0.8115034051581320575e-4 plusOrMinus 1e-16) + astrom.v.z shouldBe (0.3517555136380563427e-4 plusOrMinus 1e-16) + astrom.bm1 shouldBe (0.9999999951686012981 plusOrMinus 1e-12) + } - rc shouldBe (2.710126504531716819 plusOrMinus 1e-12) - dc shouldBe (0.1740632537627034482 plusOrMinus 1e-12) - eo shouldBe (-0.002900618712657375647 plusOrMinus 1e-14) - } - "eraS2pv" { - val pv = eraS2pv(-3.21, 0.123, 0.456, -7.8e-6, 9.01e-6, -1.23e-5) + @Test + fun eraEpv00() { + val (h, b) = eraEpv00(2400000.5, 53411.52501161) + h.position[0] shouldBe (-0.7757238809297706813 plusOrMinus 1e-14) + h.position[1] shouldBe (0.5598052241363340596 plusOrMinus 1e-14) + h.position[2] shouldBe (0.2426998466481686993 plusOrMinus 1e-14) - pv.position[0] shouldBe (-0.4514964673880165228 plusOrMinus 1e-12) - pv.position[1] shouldBe (0.0309339427734258688 plusOrMinus 1e-12) - pv.position[2] shouldBe (0.0559466810510877933 plusOrMinus 1e-12) + h.velocity[0] shouldBe (-0.1091891824147313846e-1 plusOrMinus 1e-15) + h.velocity[1] shouldBe (-0.1247187268440845008e-1 plusOrMinus 1e-15) + h.velocity[2] shouldBe (-0.5407569418065039061e-2 plusOrMinus 1e-15) - pv.velocity[0] shouldBe (0.1292270850663260170e-4 plusOrMinus 1e-16) - pv.velocity[1] shouldBe (0.2652814182060691422e-5 plusOrMinus 1e-16) - pv.velocity[2] shouldBe (0.2568431853930292259e-5 plusOrMinus 1e-16) - } - "eraStarpv" { - val pv = eraStarpv(0.01686756, -1.093989828, -1.78323516e-5, 2.336024047e-6, 0.74723.arcsec, (-21.6).kms) + b.position[0] shouldBe (-0.7714104440491111971 plusOrMinus 1e-14) + b.position[1] shouldBe (0.5598412061824171323 plusOrMinus 1e-14) + b.position[2] shouldBe (0.2425996277722452400 plusOrMinus 1e-14) + + b.velocity[0] shouldBe (-0.1091874268116823295e-1 plusOrMinus 1e-15) + b.velocity[1] shouldBe (-0.1246525461732861538e-1 plusOrMinus 1e-15) + b.velocity[2] shouldBe (-0.5404773180966231279e-2 plusOrMinus 1e-15) + } + + @Test + fun eraApcg13() { + val astrom = eraApcg13(2456165.5, 0.401182685) + + astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) + astrom.eb.x shouldBe (0.9013108747340644755 plusOrMinus 1e-12) + astrom.eb.y shouldBe (-0.4174026640406119957 plusOrMinus 1e-12) + astrom.eb.z shouldBe (-0.1809822877867817771 plusOrMinus 1e-12) + astrom.eh.x shouldBe (0.8940025429255499549 plusOrMinus 1e-12) + astrom.eh.y shouldBe (-0.4110930268331896318 plusOrMinus 1e-12) + astrom.eh.z shouldBe (-0.1782189006019749850 plusOrMinus 1e-12) + astrom.em shouldBe (1.010465295964664178 plusOrMinus 1e-12) + astrom.v.x shouldBe (0.4289638912941341125e-4 plusOrMinus 1e-16) + astrom.v.y shouldBe (0.8115034032405042132e-4 plusOrMinus 1e-16) + astrom.v.z shouldBe (0.3517555135536470279e-4 plusOrMinus 1e-16) + astrom.bm1 shouldBe (0.9999999951686013142 plusOrMinus 1e-12) + } + + @Test + fun eraAe2hd() { + val (ha, dec) = eraAe2hd(5.5.rad, 1.1.rad, 0.7.rad) + + ha shouldBe (0.5933291115507309663 plusOrMinus 1E-14) + dec shouldBe (0.9613934761647817620 plusOrMinus 1E-14) + } + + @Test + fun eraTcbTdb() { + val (whole, fraction) = eraTcbTdb(2453750.5, 0.893019599) + whole shouldBe (2453750.5 plusOrMinus 1E-6) + fraction shouldBe (0.8928551362746343397 plusOrMinus 1E-12) + } + + @Test + fun eraTcgTt() { + val (whole, fraction) = eraTcgTt(2453750.5, 0.892862531) + whole shouldBe (2453750.5 plusOrMinus 1E-6) + fraction shouldBe (0.8928551387488816828 plusOrMinus 1E-12) + } + + @Test + fun eraTdbTcb() { + val (whole, fraction) = eraTdbTcb(2453750.5, 0.892855137) + whole shouldBe (2453750.5 plusOrMinus 1E-6) + fraction shouldBe (0.8930195997253656716 plusOrMinus 1E-12) + } + + @Test + fun eraTtTcg() { + val (whole, fraction) = eraTtTcg(2453750.5, 0.892482639) + whole shouldBe (2453750.5 plusOrMinus 1E-6) + fraction shouldBe (0.8924900312508587113 plusOrMinus 1E-12) + } + + @Test + fun eraDtDb() { + val dt = eraDtDb(2448939.5, 0.123, 0.76543, 5.0123.rad, 5525.242.km, 3190.0.km) + dt shouldBe (-0.1280368005936998991e-2 plusOrMinus 1E-15) + } + + @Test + fun eraPvtob() { + val (p, v) = eraPvtob(2.0.rad, 0.5.rad, 3000.0, 1e-6.rad, (-0.5e-6).rad, 1e-8.rad, 5.0.rad) + p[0] shouldBe (4225081.367071159207 plusOrMinus 1e-5) + p[1] shouldBe (3681943.215856198144 plusOrMinus 1e-5) + p[2] shouldBe (3041149.399241260785 plusOrMinus 1e-5) + v[0] shouldBe (-268.4915389365998787 plusOrMinus 1e-5) + v[1] shouldBe (308.0977983288903123 plusOrMinus 1e-5) + v[2] shouldBe (0.0 plusOrMinus 1e-5) + } + + @Test + fun eraNut00a() { + val (psi, eps) = eraNut00a(2400000.5, 53736.0) + psi shouldBe (-0.9630909107115518431e-5 plusOrMinus 1e-13) + eps shouldBe (0.4063239174001678710e-4 plusOrMinus 1e-13) + } + + @Test + fun eraNut06a() { + val (psi, eps) = eraNut06a(2400000.5, 53736.0) + psi shouldBe (-0.9630912025820308797e-5 plusOrMinus 1e-13) + eps shouldBe (0.4063238496887249798e-4 plusOrMinus 1e-13) + } + + @Test + fun eraPnm06a() { + val rbpn = eraPnm06a(2400000.5, 50123.9999) + + rbpn[0, 0] shouldBe (0.9999995832794205484 plusOrMinus 1e-12) + rbpn[0, 1] shouldBe (0.8372382772630962111e-3 plusOrMinus 1e-14) + rbpn[0, 2] shouldBe (0.3639684771140623099e-3 plusOrMinus 1e-14) + rbpn[1, 0] shouldBe (-0.8372533744743683605e-3 plusOrMinus 1e-14) + rbpn[1, 1] shouldBe (0.9999996486492861646 plusOrMinus 1e-12) + rbpn[1, 2] shouldBe (0.4132905944611019498e-4 plusOrMinus 1e-14) + rbpn[2, 0] shouldBe (-0.3639337469629464969e-3 plusOrMinus 1e-14) + rbpn[2, 1] shouldBe (-0.4163377605910663999e-4 plusOrMinus 1e-14) + rbpn[2, 2] shouldBe (0.9999999329094260057 plusOrMinus 1e-12) + } + + @Test + fun eraAb() { + val pnat = Vector3D(-0.76321968546737951, -0.60869453983060384, -0.21676408580639883) + val v = Vector3D(2.1044018893653786e-5, -8.9108923304429319e-5, -3.8633714797716569e-5) + val ppr = eraAb(pnat, v, 0.99980921395708788.au, 0.99999999506209258) + + ppr[0] shouldBe (-0.7631631094219556269 plusOrMinus 1e-12) + ppr[1] shouldBe (-0.6087553082505590832 plusOrMinus 1e-12) + ppr[2] shouldBe (-0.2167926269368471279 plusOrMinus 1e-12) + } + + @Test + fun eraEect00() { + val eect = eraEect00(2400000.5, 53736.0) + eect shouldBe (0.2046085004885125264e-8 plusOrMinus 1e-20) + } + + @Test + fun eraEra00() { + val era00 = eraEra00(2454388.0, 0.5) + era00 shouldBe (0.4022837240028158102 plusOrMinus 1e-12) + } - pv.position[0] shouldBe (126668.5912743160601 plusOrMinus 1e-10) - pv.position[1] shouldBe (2136.792716839935195 plusOrMinus 1e-12) - pv.position[2] shouldBe (-245251.2339876830091 plusOrMinus 1e-10) + @Test + fun eraEe00() { + val ee = eraEe00(2453736.0, 0.5, 0.4090789763356509900.rad, (-0.9630909107115582393e-5).rad) + ee shouldBe (-0.8834193235367965479e-5 plusOrMinus 1e-18) + } + + @Test + fun eraGmst00() { + val theta = eraGmst00(2453736.0, 0.5, 2453736.0, 0.5) + theta shouldBe (1.754174972210740592 plusOrMinus 1e-12) + } + + @Test + fun eraGmst06() { + val theta = eraGmst06(2453736.0, 0.5, 2453736.0, 0.5) + theta shouldBe (1.754174971870091203 plusOrMinus 1e-12) + } + + @Test + fun eraEe06a() { + val ee = eraEe06a(2453736.0, 0.5) + ee shouldBe (-0.8834195072043790156e-5 plusOrMinus 1e-15) + } + + @Test + fun eraGst06a() { + val theta = eraGst06a(2453736.0, 0.5, 2453736.0, 0.5) + theta shouldBe (1.754166137675019159 plusOrMinus 1e-12) + } + + @Test + fun eraGst06() { + val rnpb = Matrix3D( + 0.9999989440476103608, -0.1332881761240011518e-2, -0.5790767434730085097e-3, + 0.1332858254308954453e-2, 0.9999991109044505944, -0.4097782710401555759e-4, + 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, + ) + + val theta = eraGst06(2453736.0, 0.5, 2453736.0, 0.5, rnpb) + theta shouldBe (1.754166138018167568 plusOrMinus 1e-12) + } + + @Test + fun eraS06() { + val s = eraS06(2400000.5, 53736.0, 0.5791308486706011000e-3, 0.4020579816732961219e-4) + s shouldBe (-0.1220032213076463117e-7 plusOrMinus 1e-18) + } + + @Test + fun eraS06a() { + val s = eraS06a(2400000.5, 52541.0) + s shouldBe (-0.1340680437291812383e-7 plusOrMinus 1e-18) + } + + @Test + fun eraEors() { + val rnpb = Matrix3D( + 0.9999989440476103608, -0.1332881761240011518e-2, -0.5790767434730085097e-3, + 0.1332858254308954453e-2, 0.9999991109044505944, -0.4097782710401555759e-4, + 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, + ) + + val eo = eraEors(rnpb, (-0.1220040848472271978e-7).rad) + eo shouldBe (-0.1332882715130744606e-2 plusOrMinus 1e-14) + } + + @Test + fun eraGst00a() { + val theta = eraGst00a(2453736.0, 0.5, 2453736.0, 0.5) + theta shouldBe (1.754166138018281369 plusOrMinus 1e-12) + } + + @Test + fun eraGst00b() { + val theta = eraGst00b(2453736.0, 0.5) + theta shouldBe (1.754166136510680589 plusOrMinus 1e-12) + } + + @Test + fun eraEe00a() { + val ee = eraEe00a(2453736.0, 0.5) + ee shouldBe (-0.8834192459222588227e-5 plusOrMinus 1e-18) + } + + @Test + fun eraEe00b() { + val ee = eraEe00b(2453736.0, 0.5) + ee shouldBe (-0.8835700060003032831e-5 plusOrMinus 1e-18) + } + + @Test + fun eraPr00() { + val (dpsipr, depspr) = eraPr00(2453736.0, 0.5) + dpsipr shouldBe (-0.8716465172668347629e-7 plusOrMinus 1e-22) + depspr shouldBe (-0.7342018386722813087e-8 plusOrMinus 1e-22) + } + + @Test + fun eraObl80() { + val eps0 = eraObl80(2454388.0, 0.5) + eps0 shouldBe (0.4090751347643816218 plusOrMinus 1e-14) + } + + @Test + fun eraNut00b() { + val (dpsi, deps) = eraNut00b(2453736.0, 0.5) + dpsi shouldBe (-0.9632552291148362783e-5 plusOrMinus 1e-13) + deps shouldBe (0.4063197106621159367e-4 plusOrMinus 1e-13) + } + + @Test + fun eraC2tcio() { + val rc2i = Matrix3D( + 0.9999998323037164738, 0.5581526271714303683e-9, -0.5791308477073443903e-3, + -0.2384266227524722273e-7, 0.9999999991917404296, -0.4020594955030704125e-4, + 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, + ) + + val rpom = Matrix3D( + 0.9999999999999674705, -0.1367174580728847031e-10, 0.2550602379999972723e-6, + 0.1414624947957029721e-10, 0.9999999999982694954, -0.1860359246998866338e-5, + -0.2550602379741215275e-6, 0.1860359247002413923e-5, 0.9999999999982369658, + ) + + val rc2t = eraC2tcio(rc2i, 1.75283325530307.rad, rpom) + + rc2t[0] shouldBe (-0.1810332128307110439 plusOrMinus 1e-12) + rc2t[1] shouldBe (0.9834769806938470149 plusOrMinus 1e-12) + rc2t[2] shouldBe (0.6555535638685466874e-4 plusOrMinus 1e-12) + rc2t[3] shouldBe (-0.9834768134135996657 plusOrMinus 1e-12) + rc2t[4] shouldBe (-0.1810332203649448367 plusOrMinus 1e-12) + rc2t[5] shouldBe (0.5749801116141106528e-3 plusOrMinus 1e-12) + rc2t[6] shouldBe (0.5773474014081407076e-3 plusOrMinus 1e-12) + rc2t[7] shouldBe (0.3961832391772658944e-4 plusOrMinus 1e-12) + rc2t[8] shouldBe (0.9999998325501691969 plusOrMinus 1e-12) + } + + @Test + fun eraEcm06() { + val rm = eraEcm06(2456165.5, 0.401182685) + + rm[0, 0] shouldBe (0.9999952427708701137 plusOrMinus 1e-14) + rm[0, 1] shouldBe (-0.2829062057663042347e-2 plusOrMinus 1e-14) + rm[0, 2] shouldBe (-0.1229163741100017629e-2 plusOrMinus 1e-14) + rm[1, 0] shouldBe (0.3084546876908653562e-2 plusOrMinus 1e-14) + rm[1, 1] shouldBe (0.9174891871550392514 plusOrMinus 1e-14) + rm[1, 2] shouldBe (0.3977487611849338124 plusOrMinus 1e-14) + rm[2, 0] shouldBe (0.2488512951527405928e-5 plusOrMinus 1e-14) + rm[2, 1] shouldBe (-0.3977506604161195467 plusOrMinus 1e-14) + rm[2, 2] shouldBe (0.9174935488232863071 plusOrMinus 1e-14) + } + + @Test + fun eraPmat06() { + val rbp = eraPmat06(2400000.5, 50123.9999) + + rbp[0, 0] shouldBe (0.9999995505176007047 plusOrMinus 1e-12) + rbp[0, 1] shouldBe (0.8695404617348208406e-3 plusOrMinus 1e-14) + rbp[0, 2] shouldBe (0.3779735201865589104e-3 plusOrMinus 1e-14) + rbp[1, 0] shouldBe (-0.8695404723772031414e-3 plusOrMinus 1e-14) + rbp[1, 1] shouldBe (0.9999996219496027161 plusOrMinus 1e-12) + rbp[1, 2] shouldBe (-0.1361752497080270143e-6 plusOrMinus 1e-14) + rbp[2, 0] shouldBe (-0.3779734957034089490e-3 plusOrMinus 1e-14) + rbp[2, 1] shouldBe (-0.1924880847894457113e-6 plusOrMinus 1e-14) + rbp[2, 2] shouldBe (0.9999999285679971958 plusOrMinus 1e-12) + } + + @Test + fun eraP06e() { + val e = eraP06e(2452541.0, 0.5) + + e.eps0 shouldBe (0.4090926006005828715 plusOrMinus 1e-14) + e.psia shouldBe (0.6664369630191613431e-3 plusOrMinus 1e-14) + e.oma shouldBe (0.4090925973783255982 plusOrMinus 1e-14) + e.bpa shouldBe (0.5561149371265209445e-6 plusOrMinus 1e-14) + e.bqa shouldBe (-0.6191517193290621270e-5 plusOrMinus 1e-14) + e.pia shouldBe (0.6216441751884382923e-5 plusOrMinus 1e-14) + e.bpia shouldBe (3.052014180023779882 plusOrMinus 1e-14) + e.epsa shouldBe (0.4090864054922431688 plusOrMinus 1e-14) + e.chia shouldBe (0.1387703379530915364e-5 plusOrMinus 1e-14) + e.za shouldBe (0.2921789846651790546e-3 plusOrMinus 1e-14) + e.zetaa shouldBe (0.3178773290332009310e-3 plusOrMinus 1e-14) + e.thetaa shouldBe (0.2650932701657497181e-3 plusOrMinus 1e-14) + e.pa shouldBe (0.6651637681381016288e-3 plusOrMinus 1e-14) + e.gam shouldBe (0.1398077115963754987e-5 plusOrMinus 1e-14) + e.phi shouldBe (0.4090864090837462602 plusOrMinus 1e-14) + e.psi shouldBe (0.6664464807480920325e-3 plusOrMinus 1e-14) + } + + @Test + fun eraNumat() { + val rmatn = eraNumat(0.4090789763356509900.rad, (-0.9630909107115582393e-5).rad, 0.4063239174001678826e-4.rad) + + rmatn[0, 0] shouldBe (0.9999999999536227949 plusOrMinus 1e-12) + rmatn[0, 1] shouldBe (0.8836239320236250577e-5 plusOrMinus 1e-12) + rmatn[0, 2] shouldBe (0.3830833447458251908e-5 plusOrMinus 1e-12) + rmatn[1, 0] shouldBe (-0.8836083657016688588e-5 plusOrMinus 1e-12) + rmatn[1, 1] shouldBe (0.9999999991354654959 plusOrMinus 1e-12) + rmatn[1, 2] shouldBe (-0.4063240865361857698e-4 plusOrMinus 1e-12) + rmatn[2, 0] shouldBe (-0.3831192481833385226e-5 plusOrMinus 1e-12) + rmatn[2, 1] shouldBe (0.4063237480216934159e-4 plusOrMinus 1e-12) + rmatn[2, 2] shouldBe (0.9999999991671660407 plusOrMinus 1e-12) + } + + @Test + fun eraNum06a() { + val rmatn = eraNum06a(2453736.0, 0.5) + + rmatn[0, 0] shouldBe (0.9999999999536227668 plusOrMinus 1e-12) + rmatn[0, 1] shouldBe (0.8836241998111535233e-5 plusOrMinus 1e-12) + rmatn[0, 2] shouldBe (0.3830834608415287707e-5 plusOrMinus 1e-12) + rmatn[1, 0] shouldBe (-0.8836086334870740138e-5 plusOrMinus 1e-12) + rmatn[1, 1] shouldBe (0.9999999991354657474 plusOrMinus 1e-12) + rmatn[1, 2] shouldBe (-0.4063240188248455065e-4 plusOrMinus 1e-12) + rmatn[2, 0] shouldBe (-0.3831193642839398128e-5 plusOrMinus 1e-12) + rmatn[2, 1] shouldBe (0.4063236803101479770e-4 plusOrMinus 1e-12) + rmatn[2, 2] shouldBe (0.9999999991671663114 plusOrMinus 1e-12) + } + + @Test + fun eraC2teqx() { + val rbpn = Matrix3D( + 0.9999989440476103608, -0.1332881761240011518e-2, -0.5790767434730085097e-3, + 0.1332858254308954453e-2, 0.9999991109044505944, -0.4097782710401555759e-4, + 0.5791308472168153320e-3, 0.4020595661593994396e-4, 0.9999998314954572365, + ) + + val rpom = Matrix3D( + 0.9999999999999674705, -0.1367174580728847031e-10, 0.2550602379999972723e-6, + 0.1414624947957029721e-10, 0.9999999999982694954, -0.1860359246998866338e-5, + -0.2550602379741215275e-6, 0.1860359247002413923e-5, 0.9999999999982369658, + ) + + val rc2t = eraC2teqx(rbpn, 1.754166138040730516.rad, rpom) + + rc2t[0, 0] shouldBe (-0.1810332128528685730 plusOrMinus 1e-12) + rc2t[0, 1] shouldBe (0.9834769806897685071 plusOrMinus 1e-12) + rc2t[0, 2] shouldBe (0.6555535639982634449e-4 plusOrMinus 1e-12) + + rc2t[1, 0] shouldBe (-0.9834768134095211257 plusOrMinus 1e-12) + rc2t[1, 1] shouldBe (-0.1810332203871023800 plusOrMinus 1e-12) + rc2t[1, 2] shouldBe (0.5749801116126438962e-3 plusOrMinus 1e-12) + + rc2t[2, 0] shouldBe (0.5773474014081539467e-3 plusOrMinus 1e-12) + rc2t[2, 1] shouldBe (0.3961832391768640871e-4 plusOrMinus 1e-12) + rc2t[2, 2] shouldBe (0.9999998325501691969 plusOrMinus 1e-12) + } + + @Test + fun eraTpors() { + val (a, b) = eraTpors((-0.03).rad, 0.07.rad, 1.3.rad, 1.5.rad).shouldNotBeNull() + + a shouldBe (4.004971075806584490 plusOrMinus 1e-13) + b shouldBe (1.565084088476417917 plusOrMinus 1e-13) + } + + @Test + fun eraTpsts() { + val (ra, dec) = eraTpsts((-0.03).rad, 0.07.rad, 2.3.rad, 1.5.rad) + + ra shouldBe (0.7596127167359629775 plusOrMinus 1e-14) + dec shouldBe (1.540864645109263028 plusOrMinus 1e-13) + } + + @Test + fun eraTporv() { + val s = CartesianCoordinate.of(1.3.rad, 1.5.rad, 1.0.au) + val v = doubleArrayOf(s.x, s.y, s.z) + + val (a, b, c) = eraTporv((-0.03).rad, 0.07.rad, v).shouldNotBeNull() + + a shouldBe (-0.003712211763801968173 plusOrMinus 1e-16) + b shouldBe (-0.004341519956299836813 plusOrMinus 1e-16) + c shouldBe (0.9999836852110587012 plusOrMinus 1e-14) + } + + @Test + fun eraTpstv() { + val s = CartesianCoordinate.of(2.3.rad, 1.5.rad, 1.0.au) + val v = doubleArrayOf(s.x, s.y, s.z) + + val (a, b, c) = eraTpstv((-0.03).rad, 0.07.rad, v).shouldNotBeNull() + + a shouldBe (0.02170030454907376677 plusOrMinus 1e-15) + b shouldBe (0.02060909590535367447 plusOrMinus 1e-15) + c shouldBe (0.999552080658352380 plusOrMinus 1e-14) + } + + @Test + fun eraTpxes() { + val (xi, eta, j) = eraTpxes(1.3.rad, 1.55.rad, 2.3.rad, 1.5.rad) + + xi shouldBe (-0.01753200983236980595 plusOrMinus 1e-15) + eta shouldBe (0.05962940005778712891 plusOrMinus 1e-15) + j shouldBeExactly 0 + } + + @Test + fun eraTpxev() { + val s = CartesianCoordinate.of(1.3.rad, 1.55.rad, 1.0.au) + val v = doubleArrayOf(s.x, s.y, s.z) + + val s0 = CartesianCoordinate.of(2.3.rad, 1.5.rad, 1.0.au) + val v0 = doubleArrayOf(s0.x, s0.y, s0.z) + + val (xi, eta, j) = eraTpxev(v, v0) + + xi shouldBe (-0.01753200983236980595 plusOrMinus 1e-15) + eta shouldBe (0.05962940005778712891 plusOrMinus 1e-15) + j shouldBeExactly 0 + } + + @Test + fun eraPb06() { + val (zeta, z, theta) = eraPb06(2400000.5, 50123.9999) + + zeta shouldBe (-0.5092634016326478238e-3 plusOrMinus 1e-12) + z shouldBe (-0.3602772060566044413e-3 plusOrMinus 1e-12) + theta shouldBe (-0.3779735537167811177e-3 plusOrMinus 1e-12) + } + + @Test + fun eraJd2Cal() { + val (y, m, d, f) = eraJd2Cal(2400000.5, 50123.9999) + y shouldBeExactly 1996 + m shouldBeExactly 2 + d shouldBeExactly 10 + f shouldBe (0.9999 plusOrMinus 1e-7) + } + + @Test + fun eraCal2Jd() { + eraCal2Jd(2003, 6, 1) shouldBeExactly 52791.0 + } + + @Test + fun eraDat() { + eraDat(2003, 6, 1, 0.0) shouldBeExactly 32.0 + eraDat(2008, 1, 17, 0.0) shouldBeExactly 33.0 + eraDat(2017, 9, 1, 0.0) shouldBeExactly 37.0 + } + + @Test + fun eraUt1Utc() { + val (u1, u2) = eraUt1Utc(2453750.5, 0.892104561, 0.3341) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8921006941018518519 plusOrMinus 1e-12) + } + + @Test + fun eraUtcTai() { + val (u1, u2) = eraUtcTai(2453750.5, 0.892100694) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8924826384444444444 plusOrMinus 1e-12) + } + + @Test + fun eraTaiUt1() { + val (u1, u2) = eraTaiUt1(2453750.5, 0.892482639, -32.6659) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8921045614537037037 plusOrMinus 1e-12) + } + + @Test + fun eraUtcUt1() { + val (u1, u2) = eraUtcUt1(2453750.5, 0.892100694, 0.3341) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8921045608981481481 plusOrMinus 1e-12) + } + + @Test + fun eraTaiUtc() { + val (u1, u2) = eraTaiUtc(2453750.5, 0.892482639) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8921006945555555556 plusOrMinus 1e-12) + } + + @Test + fun eraTaiTt() { + val (u1, u2) = eraTaiTt(2453750.5, 0.892482639) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.892855139 plusOrMinus 1e-12) + } + + @Test + fun eraTtTai() { + val (u1, u2) = eraTtTai(2453750.5, 0.892482639) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.892110139 plusOrMinus 1e-12) + } + + @Test + fun eraTtTdb() { + val (u1, u2) = eraTtTdb(2453750.5, 0.892855139, -0.000201) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8928551366736111111 plusOrMinus 1e-12) + } + + @Test + fun eraTdbTt() { + val (u1, u2) = eraTdbTt(2453750.5, 0.892855137, -0.000201) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8928551393263888889 plusOrMinus 1e-12) + } + + @Test + fun eraUt1Tai() { + val (u1, u2) = eraUt1Tai(2453750.5, 0.892104561, -32.6659) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8924826385462962963 plusOrMinus 1e-12) + } + + @Test + fun eraTtUt1() { + val (u1, u2) = eraTtUt1(2453750.5, 0.892855139, 64.8499) + u1 shouldBeExactly 2453750.5 + u2 shouldBe (0.8921045614537037037 plusOrMinus 1e-12) + } + + @Test + fun eraBp00() { + val (rb, rp, rbp) = eraBp00(2400000.5, 50123.9999) + + rb[0, 0] shouldBe (0.9999999999999942498 plusOrMinus 1e-12) + rb[0, 1] shouldBe (-0.7078279744199196626e-7 plusOrMinus 1e-16) + rb[0, 2] shouldBe (0.8056217146976134152e-7 plusOrMinus 1e-16) + rb[1, 0] shouldBe (0.7078279477857337206e-7 plusOrMinus 1e-16) + rb[1, 1] shouldBe (0.9999999999999969484 plusOrMinus 1e-12) + rb[1, 2] shouldBe (0.3306041454222136517e-7 plusOrMinus 1e-16) + rb[2, 0] shouldBe (-0.8056217380986972157e-7 plusOrMinus 1e-16) + rb[2, 1] shouldBe (-0.3306040883980552500e-7 plusOrMinus 1e-16) + rb[2, 2] shouldBe (0.9999999999999962084 plusOrMinus 1e-12) + rp[0, 0] shouldBe (0.9999995504864048241 plusOrMinus 1e-12) + rp[0, 1] shouldBe (0.8696113836207084411e-3 plusOrMinus 1e-14) + rp[0, 2] shouldBe (0.3778928813389333402e-3 plusOrMinus 1e-14) + rp[1, 0] shouldBe (-0.8696113818227265968e-3 plusOrMinus 1e-14) + rp[1, 1] shouldBe (0.9999996218879365258 plusOrMinus 1e-12) + rp[1, 2] shouldBe (-0.1690679263009242066e-6 plusOrMinus 1e-14) + rp[2, 0] shouldBe (-0.3778928854764695214e-3 plusOrMinus 1e-14) + rp[2, 1] shouldBe (-0.1595521004195286491e-6 plusOrMinus 1e-14) + rp[2, 2] shouldBe (0.9999999285984682756 plusOrMinus 1e-12) + rbp[0, 0] shouldBe (0.9999995505175087260 plusOrMinus 1e-12) + rbp[0, 1] shouldBe (0.8695405883617884705e-3 plusOrMinus 1e-14) + rbp[0, 2] shouldBe (0.3779734722239007105e-3 plusOrMinus 1e-14) + rbp[1, 0] shouldBe (-0.8695405990410863719e-3 plusOrMinus 1e-14) + rbp[1, 1] shouldBe (0.9999996219494925900 plusOrMinus 1e-12) + rbp[1, 2] shouldBe (-0.1360775820404982209e-6 plusOrMinus 1e-14) + rbp[2, 0] shouldBe (-0.3779734476558184991e-3 plusOrMinus 1e-14) + rbp[2, 1] shouldBe (-0.1925857585832024058e-6 plusOrMinus 1e-14) + rbp[2, 2] shouldBe (0.9999999285680153377 plusOrMinus 1e-12) + } + + @Test + fun eraPn00() { + val (_, _, epsa, rb, rp, rbp, rn, rbpn) = eraPn00(2400000.5, 53736.0, -0.9632552291149335877e-5, 0.4063197106621141414e-4) + + epsa shouldBe (0.4090791789404229916 plusOrMinus 1e-12) + + rb[0, 0] shouldBe (0.9999999999999942498 plusOrMinus 1e-12) + rb[0, 1] shouldBe (-0.7078279744199196626e-7 plusOrMinus 1e-18) + rb[0, 2] shouldBe (0.8056217146976134152e-7 plusOrMinus 1e-18) + + rb[1, 0] shouldBe (0.7078279477857337206e-7 plusOrMinus 1e-18) + rb[1, 1] shouldBe (0.9999999999999969484 plusOrMinus 1e-12) + rb[1, 2] shouldBe (0.3306041454222136517e-7 plusOrMinus 1e-18) + + rb[2, 0] shouldBe (-0.8056217380986972157e-7 plusOrMinus 1e-18) + rb[2, 1] shouldBe (-0.3306040883980552500e-7 plusOrMinus 1e-18) + rb[2, 2] shouldBe (0.9999999999999962084 plusOrMinus 1e-12) + + rp[0, 0] shouldBe (0.9999989300532289018 plusOrMinus 1e-12) + rp[0, 1] shouldBe (-0.1341647226791824349e-2 plusOrMinus 1e-14) + rp[0, 2] shouldBe (-0.5829880927190296547e-3 plusOrMinus 1e-14) + + rp[1, 0] shouldBe (0.1341647231069759008e-2 plusOrMinus 1e-14) + rp[1, 1] shouldBe (0.9999990999908750433 plusOrMinus 1e-12) + rp[1, 2] shouldBe (-0.3837444441583715468e-6 plusOrMinus 1e-14) + + rp[2, 0] shouldBe (0.5829880828740957684e-3 plusOrMinus 1e-14) + rp[2, 1] shouldBe (-0.3984203267708834759e-6 plusOrMinus 1e-14) + rp[2, 2] shouldBe (0.9999998300623538046 plusOrMinus 1e-12) + + rbp[0, 0] shouldBe (0.9999989300052243993 plusOrMinus 1e-12) + rbp[0, 1] shouldBe (-0.1341717990239703727e-2 plusOrMinus 1e-14) + rbp[0, 2] shouldBe (-0.5829075749891684053e-3 plusOrMinus 1e-14) + + rbp[1, 0] shouldBe (0.1341718013831739992e-2 plusOrMinus 1e-14) + rbp[1, 1] shouldBe (0.9999990998959191343 plusOrMinus 1e-12) + rbp[1, 2] shouldBe (-0.3505759733565421170e-6 plusOrMinus 1e-14) + + rbp[2, 0] shouldBe (0.5829075206857717883e-3 plusOrMinus 1e-14) + rbp[2, 1] shouldBe (-0.4315219955198608970e-6 plusOrMinus 1e-14) + rbp[2, 2] shouldBe (0.9999998301093036269 plusOrMinus 1e-12) + + rn[0, 0] shouldBe (0.9999999999536069682 plusOrMinus 1e-12) + rn[0, 1] shouldBe (0.8837746144872140812e-5 plusOrMinus 1e-16) + rn[0, 2] shouldBe (0.3831488838252590008e-5 plusOrMinus 1e-16) + + rn[1, 0] shouldBe (-0.8837590456633197506e-5 plusOrMinus 1e-16) + rn[1, 1] shouldBe (0.9999999991354692733 plusOrMinus 1e-12) + rn[1, 2] shouldBe (-0.4063198798559573702e-4 plusOrMinus 1e-16) + + rn[2, 0] shouldBe (-0.3831847930135328368e-5 plusOrMinus 1e-16) + rn[2, 1] shouldBe (0.4063195412258150427e-4 plusOrMinus 1e-16) + rn[2, 2] shouldBe (0.9999999991671806225 plusOrMinus 1e-12) + + rbpn[0, 0] shouldBe (0.9999989440499982806 plusOrMinus 1e-12) + rbpn[0, 1] shouldBe (-0.1332880253640848301e-2 plusOrMinus 1e-14) + rbpn[0, 2] shouldBe (-0.5790760898731087295e-3 plusOrMinus 1e-14) + + rbpn[1, 0] shouldBe (0.1332856746979948745e-2 plusOrMinus 1e-14) + rbpn[1, 1] shouldBe (0.9999991109064768883 plusOrMinus 1e-12) + rbpn[1, 2] shouldBe (-0.4097740555723063806e-4 plusOrMinus 1e-14) + + rbpn[2, 0] shouldBe (0.5791301929950205000e-3 plusOrMinus 1e-14) + rbpn[2, 1] shouldBe (0.4020553681373702931e-4 plusOrMinus 1e-14) + rbpn[2, 2] shouldBe (0.9999998314958529887 plusOrMinus 1e-12) + } + + @Test + fun eraC2tpe() { + val rc2t = + eraC2tpe(2400000.5, 53736.0, 2400000.5, 53736.0, -0.9630909107115582393e-5, 0.4090789763356509900, 2.55060238e-7, 1.860359247e-6) + + rc2t[0, 0] shouldBe (-0.1813677995763029394 plusOrMinus 1e-12) + rc2t[0, 1] shouldBe (0.9023482206891683275 plusOrMinus 1e-12) + rc2t[0, 2] shouldBe (-0.3909902938641085751 plusOrMinus 1e-12) + + rc2t[1, 0] shouldBe (-0.9834147641476804807 plusOrMinus 1e-12) + rc2t[1, 1] shouldBe (-0.1659883635434995121 plusOrMinus 1e-12) + rc2t[1, 2] shouldBe (0.7309763898042819705e-1 plusOrMinus 1e-12) + + rc2t[2, 0] shouldBe (0.1059685430673215247e-2 plusOrMinus 1e-12) + rc2t[2, 1] shouldBe (0.3977631855605078674 plusOrMinus 1e-12) + rc2t[2, 2] shouldBe (0.9174875068792735362 plusOrMinus 1e-12) + } + + @Test + fun eraS00() { + val s = eraS00(2400000.5, 53736.0, 0.5791308486706011000e-3, 0.4020579816732961219e-4) + s shouldBe (-0.1220036263270905693e-7 plusOrMinus 1e-18) + } + + @Test + fun eraS00b() { + val s = eraS00b(2400000.5, 52541.0) + s shouldBe (-0.1340695782951026584e-7 plusOrMinus 1e-18) + } + + @Test + fun eraS00a() { + val s = eraS00a(2400000.5, 52541.0) + s shouldBe (-0.1340684448919163584e-7 plusOrMinus 1e-18) + } + + @Test + fun eraApco13() { + val (astrom, eo) = eraApco13( + 2456384.5, 0.969254051, 0.1550675, + -0.527800806, -1.2345856, 2738.0, + 2.47230737e-7, 1.82640464e-6, + 731.0, 12.8, 0.59, 0.55 + ) + + astrom.pmt shouldBe (13.25248468622475727 plusOrMinus 1e-11) + astrom.eb.x shouldBe (-0.9741827107320875162 plusOrMinus 1e-12) + astrom.eb.y shouldBe (-0.2115130190489716682 plusOrMinus 1e-12) + astrom.eb.z shouldBe (-0.09179840189496755339 plusOrMinus 1e-12) + astrom.eh.x shouldBe (-0.9736425572586935247 plusOrMinus 1e-12) + astrom.eh.y shouldBe (-0.2092452121603336166 plusOrMinus 1e-12) + astrom.eh.z shouldBe (-0.09075578153885665295 plusOrMinus 1e-12) + astrom.em shouldBe (0.9998233240913898141 plusOrMinus 1e-12) + astrom.v.x shouldBe (0.2078704994520489246e-4 plusOrMinus 1e-16) + astrom.v.y shouldBe (-0.8955360133238868938e-4 plusOrMinus 1e-16) + astrom.v.z shouldBe (-0.3863338993055887398e-4 plusOrMinus 1e-16) + astrom.bm1 shouldBe (0.9999999950277561004 plusOrMinus 1e-12) + astrom.bpn[0, 0] shouldBe (0.9999991390295147999 plusOrMinus 1e-12) + astrom.bpn[1, 0] shouldBe (0.4978650075315529277e-7 plusOrMinus 1e-12) + astrom.bpn[2, 0] shouldBe (0.001312227200850293372 plusOrMinus 1e-12) + astrom.bpn[0, 1] shouldBe (-0.1136336652812486604e-7 plusOrMinus 1e-12) + astrom.bpn[1, 1] shouldBe (0.9999999995713154865 plusOrMinus 1e-12) + astrom.bpn[2, 1] shouldBe (-0.2928086230975367296e-4 plusOrMinus 1e-12) + astrom.bpn[0, 2] shouldBe (-0.001312227201745553566 plusOrMinus 1e-12) + astrom.bpn[1, 2] shouldBe (0.2928082218847679162e-4 plusOrMinus 1e-12) + astrom.bpn[2, 2] shouldBe (0.9999991386008312212 plusOrMinus 1e-12) + astrom.along shouldBe (-0.5278008060295995733 plusOrMinus 1e-12) + astrom.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) + astrom.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) + astrom.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) + astrom.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) + astrom.diurab shouldBeExactly 0.0 + astrom.eral shouldBe (2.617608909189664000 plusOrMinus 1e-12) + astrom.refa shouldBe (0.2014187785940396921e-3 plusOrMinus 1e-15) + astrom.refb shouldBe (-0.2361408314943696227e-6 plusOrMinus 1e-18) + eo shouldBe (-0.003020548354802412839 plusOrMinus 1e-14) + } + + @Test + fun eraPmpx() { + val pco = eraPmpx(1.234, 0.789, 1e-5, -2e-5, 1e-2.arcsec, 10.0.kms, 8.75, Vector3D(0.9, 0.4, 0.1)) + pco[0] shouldBe (0.2328137623960308438 plusOrMinus 1e-12) + pco[1] shouldBe (0.6651097085397855328 plusOrMinus 1e-12) + pco[2] shouldBe (0.7095257765896359837 plusOrMinus 1e-12) + } + + @Test + fun eraAtciq() { + val (astrom) = eraApci13(2456165.5, 0.401182685) + val (ri, di) = eraAtciq(2.71, 0.174, 1e-5, 5e-6, 0.1.arcsec, 55.0.kms, astrom) + ri shouldBe (2.710121572968696744 plusOrMinus 1e-12) + di shouldBe (0.1729371367219539137 plusOrMinus 1e-12) + } + + @Test + fun eraAtciqz() { + val (astrom) = eraApci13(2456165.5, 0.401182685) + val (ri, di) = eraAtciqz(2.71, 0.174, astrom) + ri shouldBe (2.709994899247256984 plusOrMinus 1e-12) + di shouldBe (0.1728740720984931891 plusOrMinus 1e-12) + } + + @Test + fun eraAtci13() { + val (ri, di, eo) = eraAtci13(2.71, 0.174, 1e-5, 5e-6, 0.1.arcsec, 55.0.kms, 2456165.5, 0.401182685) + ri shouldBe (2.710121572968696744 plusOrMinus 1e-12) + di shouldBe (0.1729371367219539137 plusOrMinus 1e-12) + eo shouldBe (-0.002900618712657375647 plusOrMinus 1e-14) + } + + @Test + fun eraApci13() { + val (astrom, eo) = eraApci13(2456165.5, 0.401182685) + + astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) + astrom.eb[0] shouldBe (0.9013108747340644755 plusOrMinus 1e-12) + astrom.eb[1] shouldBe (-0.4174026640406119957 plusOrMinus 1e-12) + astrom.eb[2] shouldBe (-0.1809822877867817771 plusOrMinus 1e-12) + astrom.eh[0] shouldBe (0.8940025429255499549 plusOrMinus 1e-12) + astrom.eh[1] shouldBe (-0.4110930268331896318 plusOrMinus 1e-12) + astrom.eh[2] shouldBe (-0.1782189006019749850 plusOrMinus 1e-12) + astrom.em shouldBe (1.010465295964664178 plusOrMinus 1e-12) + astrom.v[0] shouldBe (0.4289638912941341125e-4 plusOrMinus 1e-16) + astrom.v[1] shouldBe (0.8115034032405042132e-4 plusOrMinus 1e-16) + astrom.v[2] shouldBe (0.3517555135536470279e-4 plusOrMinus 1e-16) + astrom.bm1 shouldBe (0.9999999951686013142 plusOrMinus 1e-12) + astrom.bpn[0, 0] shouldBe (0.9999992060376761710 plusOrMinus 1e-12) + astrom.bpn[1, 0] shouldBe (0.4124244860106037157e-7 plusOrMinus 1e-12) + astrom.bpn[2, 0] shouldBe (0.1260128571051709670e-2 plusOrMinus 1e-12) + astrom.bpn[0, 1] shouldBe (-0.1282291987222130690e-7 plusOrMinus 1e-12) + astrom.bpn[1, 1] shouldBe (0.9999999997456835325 plusOrMinus 1e-12) + astrom.bpn[2, 1] shouldBe (-0.2255288829420524935e-4 plusOrMinus 1e-12) + astrom.bpn[0, 2] shouldBe (-0.1260128571661374559e-2 plusOrMinus 1e-12) + astrom.bpn[1, 2] shouldBe (0.2255285422953395494e-4 plusOrMinus 1e-12) + astrom.bpn[2, 2] shouldBe (0.9999992057833604343 plusOrMinus 1e-12) + eo shouldBe (-0.2900618712657375647e-2 plusOrMinus 1e-12) + } + + @Test + fun eraLdsun() { + val p = Vector3D(-0.763276255, -0.608633767, -0.216735543) + val e = Vector3D(-0.973644023, -0.20925523, -0.0907169552) + val p1 = eraLdsun(p, e, 0.999809214) + p1[0] shouldBe (-0.7632762580731413169 plusOrMinus 1e-12) + p1[1] shouldBe (-0.6086337635262647900 plusOrMinus 1e-12) + p1[2] shouldBe (-0.2167355419322321302 plusOrMinus 1e-12) + } + + @Test + fun eraLd() { + val p = Vector3D(-0.763276255, -0.608633767, -0.216735543) + val q = Vector3D(-0.763276255, -0.608633767, -0.216735543) + val e = Vector3D(0.76700421, 0.605629598, 0.211937094) + val p1 = eraLd(0.00028574, p, q, e, 8.91276983, 3e-10) + p1[0] shouldBe (-0.7632762548968159627 plusOrMinus 1e-12) + p1[1] shouldBe (-0.6086337670823762701 plusOrMinus 1e-12) + p1[2] shouldBe (-0.2167355431320546947 plusOrMinus 1e-12) + } + + @Test + fun eraApci() { + val ebp = Vector3D(0.901310875, -0.417402664, -0.180982288) + val ebv = Vector3D(0.00742727954, 0.0140507459, 0.00609045792) + val ehp = Vector3D(0.903358544, -0.415395237, -0.180084014) + val astrom = eraApci(2456165.5, 0.401182685, ebp, ebv, ehp, 0.0013122272, -2.92808623e-5, 3.05749468e-8) + + astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) + astrom.eb[0] shouldBe (0.901310875 plusOrMinus 1e-12) + astrom.eb[1] shouldBe (-0.417402664 plusOrMinus 1e-12) + astrom.eb[2] shouldBe (-0.180982288 plusOrMinus 1e-12) + astrom.eh[0] shouldBe (0.8940025429324143045 plusOrMinus 1e-12) + astrom.eh[1] shouldBe (-0.4110930268679817955 plusOrMinus 1e-12) + astrom.eh[2] shouldBe (-0.1782189004872870264 plusOrMinus 1e-12) + astrom.em shouldBe (1.010465295811013146 plusOrMinus 1e-12) + astrom.v[0] shouldBe (0.4289638913597693554e-4 plusOrMinus 1e-16) + astrom.v[1] shouldBe (0.8115034051581320575e-4 plusOrMinus 1e-16) + astrom.v[2] shouldBe (0.3517555136380563427e-4 plusOrMinus 1e-16) + astrom.bm1 shouldBe (0.9999999951686012981 plusOrMinus 1e-12) + astrom.bpn[0, 0] shouldBe (0.9999991390295159156 plusOrMinus 1e-12) + astrom.bpn[1, 0] shouldBe (0.4978650072505016932e-7 plusOrMinus 1e-12) + astrom.bpn[2, 0] shouldBe (0.1312227200000000000e-2 plusOrMinus 1e-12) + astrom.bpn[0, 1] shouldBe (-0.1136336653771609630e-7 plusOrMinus 1e-12) + astrom.bpn[1, 1] shouldBe (0.9999999995713154868 plusOrMinus 1e-12) + astrom.bpn[2, 1] shouldBe (-0.2928086230000000000e-4 plusOrMinus 1e-12) + astrom.bpn[0, 2] shouldBe (-0.1312227200895260194e-2 plusOrMinus 1e-12) + astrom.bpn[1, 2] shouldBe (0.2928082217872315680e-4 plusOrMinus 1e-12) + astrom.bpn[2, 2] shouldBe (0.9999991386008323373 plusOrMinus 1e-12) + } + + @Test + fun eraApcs13() { + val p = Vector3D(-6241497.16, 401346.896, -1251136.04) + val v = Vector3D(-29.264597, -455.021831, 0.0266151194) + val astrom = eraApcs13(2456165.5, 0.401182685, p, v) + + astrom.pmt shouldBe (12.65133794027378508 plusOrMinus 1e-11) + astrom.eb[0] shouldBe (0.9012691529025250644 plusOrMinus 1e-12) + astrom.eb[1] shouldBe (-0.4173999812023194317 plusOrMinus 1e-12) + astrom.eb[2] shouldBe (-0.1809906511146429670 plusOrMinus 1e-12) + astrom.eh[0] shouldBe (0.8939939101760130792 plusOrMinus 1e-12) + astrom.eh[1] shouldBe (-0.4111053891734021478 plusOrMinus 1e-12) + astrom.eh[2] shouldBe (-0.1782336880636997374 plusOrMinus 1e-12) + astrom.em shouldBe (1.010428384373491095 plusOrMinus 1e-12) + astrom.v[0] shouldBe (0.4279877294121697570e-4 plusOrMinus 1e-16) + astrom.v[1] shouldBe (0.7963255087052120678e-4 plusOrMinus 1e-16) + astrom.v[2] shouldBe (0.3517564013384691531e-4 plusOrMinus 1e-16) + astrom.bm1 shouldBe (0.9999999952947980978 plusOrMinus 1e-12) + astrom.bpn shouldBe Matrix3D.IDENTITY + } + + @Test + fun eraAtioq() { + val astrom = + eraApio13(2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55) + val (aob, zob, hob, dob, rob) = eraAtioq(2.710121572969038991, 0.1729371367218230438, astrom) + + aob shouldBe (0.9233952224895122499e-1 plusOrMinus 1e-12) + zob shouldBe (1.407758704513549991 plusOrMinus 1e-12) + hob shouldBe (-0.9247619879881698140e-1 plusOrMinus 1e-12) + dob shouldBe (0.1717653435756234676 plusOrMinus 1e-12) + rob shouldBe (2.710085107988480746 plusOrMinus 1e-12) + } + + @Test + fun eraApio13() { + val astrom = + eraApio13(2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55) + + astrom.along shouldBe (-0.5278008060295995733 plusOrMinus 1e-12) + astrom.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) + astrom.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) + astrom.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) + astrom.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) + astrom.diurab shouldBe (0.5135843661699913529e-6 plusOrMinus 1e-12) + astrom.eral shouldBe (2.617608909189664000 plusOrMinus 1e-12) + astrom.refa shouldBe (0.2014187785940396921e-3 plusOrMinus 1e-15) + astrom.refb shouldBe (-0.2361408314943696227e-6 plusOrMinus 1e-18) + } + + @Test + fun eraApio() { + val astrom = + eraApio(-3.01974337e-11, 3.14540971, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 0.000201418779, -2.36140831e-7) + + astrom.along shouldBe (-0.5278008060295995734 plusOrMinus 1e-12) + astrom.xpl shouldBe (0.1133427418130752958e-5 plusOrMinus 1e-17) + astrom.ypl shouldBe (0.1453347595780646207e-5 plusOrMinus 1e-17) + astrom.sphi shouldBe (-0.9440115679003211329 plusOrMinus 1e-12) + astrom.cphi shouldBe (0.3299123514971474711 plusOrMinus 1e-12) + astrom.diurab shouldBe (0.5135843661699913529e-6 plusOrMinus 1e-12) + astrom.eral shouldBe (2.617608903970400427 plusOrMinus 1e-12) + astrom.refa shouldBe (0.2014187790000000000e-3 plusOrMinus 1e-15) + astrom.refb shouldBe (-0.2361408310000000000e-6 plusOrMinus 1e-18) + } + + @Test + fun eraAtco13() { + val (b, eo) = eraAtco13( + 2.71, 0.174, 1e-5, 5e-6, 0.1.arcsec, 55.0.kms, 2456384.5, 0.969254051, + 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, + 0.59, 0.55 + ) + + val (aob, zob, hob, dob, rob) = b + + aob shouldBe (0.9251774485485515207e-1 plusOrMinus 1e-12) + zob shouldBe (1.407661405256499357 plusOrMinus 1e-12) + hob shouldBe (-0.9265154431529724692e-1 plusOrMinus 1e-12) + dob shouldBe (0.1716626560072526200 plusOrMinus 1e-12) + rob shouldBe (2.710260453504961012 plusOrMinus 1e-12) + eo shouldBe (-0.003020548354802412839 plusOrMinus 1e-14) + } + + @Test + fun eraAticq() { + val (astrom) = eraApci13(2456165.5, 0.401182685) + val (ri, di) = eraAticq(2.710121572969038991, 0.1729371367218230438, astrom) + ri shouldBe (2.710126504531716819 plusOrMinus 1e-12) + di shouldBe (0.1740632537627034482 plusOrMinus 1e-12) + } + + @Test + fun eraAtic13() { + val (rc, dc, eo) = eraAtic13(2.710121572969038991, 0.1729371367218230438, 2456165.5, 0.401182685) + + rc shouldBe (2.710126504531716819 plusOrMinus 1e-12) + dc shouldBe (0.1740632537627034482 plusOrMinus 1e-12) + eo shouldBe (-0.002900618712657375647 plusOrMinus 1e-14) + } + + @Test + fun eraS2pv() { + val pv = eraS2pv(-3.21, 0.123, 0.456, -7.8e-6, 9.01e-6, -1.23e-5) + + pv.position[0] shouldBe (-0.4514964673880165228 plusOrMinus 1e-12) + pv.position[1] shouldBe (0.0309339427734258688 plusOrMinus 1e-12) + pv.position[2] shouldBe (0.0559466810510877933 plusOrMinus 1e-12) + + pv.velocity[0] shouldBe (0.1292270850663260170e-4 plusOrMinus 1e-16) + pv.velocity[1] shouldBe (0.2652814182060691422e-5 plusOrMinus 1e-16) + pv.velocity[2] shouldBe (0.2568431853930292259e-5 plusOrMinus 1e-16) + } + + @Test + fun eraStarpv() { + val pv = eraStarpv(0.01686756, -1.093989828, -1.78323516e-5, 2.336024047e-6, 0.74723.arcsec, (-21.6).kms) + + pv.position[0] shouldBe (126668.5912743160601 plusOrMinus 1e-10) + pv.position[1] shouldBe (2136.792716839935195 plusOrMinus 1e-12) + pv.position[2] shouldBe (-245251.2339876830091 plusOrMinus 1e-10) + + pv.velocity[0] shouldBe (-0.4051854008955659551e-2 plusOrMinus 1e-13) + pv.velocity[1] shouldBe (-0.6253919754414777970e-2 plusOrMinus 1e-15) + pv.velocity[2] shouldBe (0.1189353714588109341e-1 plusOrMinus 1e-13) + } + + @Test + fun eraAtoiq() { + val astrom = + eraApio13(2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55) + + with(eraAtoiq('R', 2.710085107986886201, 0.1717653435758265198, astrom)) { + this[0] shouldBe (2.710121574447540810 plusOrMinus 1e-12) + this[1] shouldBe (0.17293718391166087785 plusOrMinus 1e-12) + } + with(eraAtoiq('H', -0.09247619879782006106, 0.1717653435758265198, astrom)) { + this[0] shouldBe (2.710121574448138676 plusOrMinus 1e-12) + this[1] shouldBe (0.1729371839116608778 plusOrMinus 1e-12) + } + with(eraAtoiq('A', 0.09233952224794989993, 1.407758704513722461, astrom)) { + this[0] shouldBe (2.710121574448138676 plusOrMinus 1e-12) + this[1] shouldBe (0.1729371839116608781 plusOrMinus 1e-12) + } + } - pv.velocity[0] shouldBe (-0.4051854008955659551e-2 plusOrMinus 1e-13) - pv.velocity[1] shouldBe (-0.6253919754414777970e-2 plusOrMinus 1e-15) - pv.velocity[2] shouldBe (0.1189353714588109341e-1 plusOrMinus 1e-13) + @Test + fun eraAtoc13() { + // @formatter:off + with(eraAtoc13('R', 2.710085107986886201, 0.1717653435758265198, 2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55)) { + this[0] shouldBe (2.709956744659136129 plusOrMinus 1e-12) + this[1] shouldBe (0.1741696500898471362 plusOrMinus 1e-12) } - "eraAtoiq" { - val astrom = - eraApio13(2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55) - - with(eraAtoiq('R', 2.710085107986886201, 0.1717653435758265198, astrom)) { - this[0] shouldBe (2.710121574447540810 plusOrMinus 1e-12) - this[1] shouldBe (0.17293718391166087785 plusOrMinus 1e-12) - } - with(eraAtoiq('H', -0.09247619879782006106, 0.1717653435758265198, astrom)) { - this[0] shouldBe (2.710121574448138676 plusOrMinus 1e-12) - this[1] shouldBe (0.1729371839116608778 plusOrMinus 1e-12) - } - with(eraAtoiq('A', 0.09233952224794989993, 1.407758704513722461, astrom)) { - this[0] shouldBe (2.710121574448138676 plusOrMinus 1e-12) - this[1] shouldBe (0.1729371839116608781 plusOrMinus 1e-12) - } + with(eraAtoc13('H', -0.09247619879782006106, 0.1717653435758265198, 2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55)) { + this[0] shouldBe (2.709956744659734086 plusOrMinus 1e-12) + this[1] shouldBe (0.1741696500898471362 plusOrMinus 1e-12) } - "eraAtoc13" { - // @formatter:off - with(eraAtoc13('R', 2.710085107986886201, 0.1717653435758265198, 2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55)) { - this[0] shouldBe (2.709956744659136129 plusOrMinus 1e-12) - this[1] shouldBe (0.1741696500898471362 plusOrMinus 1e-12) - } - with(eraAtoc13('H', -0.09247619879782006106, 0.1717653435758265198, 2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55)) { - this[0] shouldBe (2.709956744659734086 plusOrMinus 1e-12) - this[1] shouldBe (0.1741696500898471362 plusOrMinus 1e-12) - } - with(eraAtoc13('A', 0.09233952224794989993, 1.407758704513722461, 2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55)) { - this[0] shouldBe (2.709956744659734086 plusOrMinus 1e-12) - this[1] shouldBe (0.1741696500898471366 plusOrMinus 1e-12) - } - // @formatter:on + with(eraAtoc13('A', 0.09233952224794989993, 1.407758704513722461, 2456384.5, 0.969254051, 0.1550675, -0.527800806, -1.2345856, 2738.0, 2.47230737e-7, 1.82640464e-6, 731.0, 12.8, 0.59, 0.55)) { + this[0] shouldBe (2.709956744659734086 plusOrMinus 1e-12) + this[1] shouldBe (0.1741696500898471366 plusOrMinus 1e-12) } + // @formatter:on } } diff --git a/nebulosa-fits/src/test/kotlin/FitsFormatTest.kt b/nebulosa-fits/src/test/kotlin/FitsFormatTest.kt index 081321c31..1cad69e9a 100644 --- a/nebulosa-fits/src/test/kotlin/FitsFormatTest.kt +++ b/nebulosa-fits/src/test/kotlin/FitsFormatTest.kt @@ -1,14 +1,15 @@ import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import nebulosa.fits.isFits -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.M82_COLOR_16_XISF +import nebulosa.test.NGC3344_COLOR_8_FITS +import org.junit.jupiter.api.Test -class FitsFormatTest : AbstractFitsAndXisfTest() { +class FitsFormatTest { - init { - "should be fits format" { - NGC3344_COLOR_8_FITS.isFits().shouldBeTrue() - M82_COLOR_16_XISF.isFits().shouldBeFalse() - } + @Test + fun shouldBeFitsFormat() { + NGC3344_COLOR_8_FITS.isFits().shouldBeTrue() + M82_COLOR_16_XISF.isFits().shouldBeFalse() } } diff --git a/nebulosa-fits/src/test/kotlin/FitsHeaderCardFormatterTest.kt b/nebulosa-fits/src/test/kotlin/FitsHeaderCardFormatterTest.kt index 54d099a1c..8a7ced33f 100644 --- a/nebulosa-fits/src/test/kotlin/FitsHeaderCardFormatterTest.kt +++ b/nebulosa-fits/src/test/kotlin/FitsHeaderCardFormatterTest.kt @@ -1,30 +1,37 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import nebulosa.fits.FitsHeaderCard import nebulosa.fits.FitsHeaderCardFormatter +import org.junit.jupiter.api.Test -class FitsHeaderCardFormatterTest : StringSpec() { +class FitsHeaderCardFormatterTest { - init { - "should format boolean value" { - val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("SIMPLE", true, "Boolean Type")) - text shouldBe "SIMPLE = T / Boolean Type " - } - "should format integer value" { - val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("NAXIS", 3, "Integer Type")) - text shouldBe "NAXIS = 3 / Integer Type " - } - "should format decimal value" { - val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("EXPOSURE", 150.0, "Decimal Type")) - text shouldBe "EXPOSURE= 150.0 / Decimal Type " - } - "should format text value" { - val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("INSTRUME", "Camera", "Text Type")) - text shouldBe "INSTRUME= 'Camera ' / Text Type " - } - "should format end key" { - val text = FitsHeaderCardFormatter.format(FitsHeaderCard.END) - text shouldBe "END " - } + @Test + fun shouldFormatBooleanValue() { + val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("SIMPLE", true, "Boolean Type")) + text shouldBe "SIMPLE = T / Boolean Type " + } + + @Test + fun shouldFormatIntegerValue() { + val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("NAXIS", 3, "Integer Type")) + text shouldBe "NAXIS = 3 / Integer Type " + } + + @Test + fun shouldFormatDecimalValue() { + val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("EXPOSURE", 150.0, "Decimal Type")) + text shouldBe "EXPOSURE= 150.0 / Decimal Type " + } + + @Test + fun shouldFormatTextValue() { + val text = FitsHeaderCardFormatter.format(FitsHeaderCard.create("INSTRUME", "Camera", "Text Type")) + text shouldBe "INSTRUME= 'Camera ' / Text Type " + } + + @Test + fun shouldFormatEndKey() { + val text = FitsHeaderCardFormatter.format(FitsHeaderCard.END) + text shouldBe "END " } } diff --git a/nebulosa-fits/src/test/kotlin/FitsHeaderCardParserTest.kt b/nebulosa-fits/src/test/kotlin/FitsHeaderCardParserTest.kt index 57ac0aabb..c2f522873 100644 --- a/nebulosa-fits/src/test/kotlin/FitsHeaderCardParserTest.kt +++ b/nebulosa-fits/src/test/kotlin/FitsHeaderCardParserTest.kt @@ -1,50 +1,57 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import nebulosa.fits.FitsHeaderCardParser import nebulosa.fits.FitsHeaderCardType +import org.junit.jupiter.api.Test -class FitsHeaderCardParserTest : StringSpec() { - - init { - "should parse boolean value" { - val parsed = FitsHeaderCardParser("SIMPLE = T / Boolean Type ") - - parsed.key shouldBe "SIMPLE" - parsed.value shouldBe "T" - parsed.type shouldBe FitsHeaderCardType.BOOLEAN - parsed.comment shouldBe "Boolean Type" - } - "should parse integer value" { - val parsed = FitsHeaderCardParser("NAXIS = 3 / Integer Type ") - - parsed.key shouldBe "NAXIS" - parsed.value shouldBe "3" - parsed.type shouldBe FitsHeaderCardType.INTEGER - parsed.comment shouldBe "Integer Type" - } - "should parse decimal value" { - val parsed = FitsHeaderCardParser("EXPOSURE= 150.0 / Decimal Type ") - - parsed.key shouldBe "EXPOSURE" - parsed.value shouldBe "150.0" - parsed.type shouldBe FitsHeaderCardType.DECIMAL - parsed.comment shouldBe "Decimal Type" - } - "should parse text value" { - val parsed = FitsHeaderCardParser("INSTRUME= 'Camera ' / Text Type ") - - parsed.key shouldBe "INSTRUME" - parsed.value shouldBe "Camera" - parsed.type shouldBe FitsHeaderCardType.TEXT - parsed.comment shouldBe "Text Type" - } - "should parse end key" { - val parsed = FitsHeaderCardParser("END") - - parsed.key shouldBe "END" - parsed.value shouldBe "" - parsed.type shouldBe FitsHeaderCardType.NONE - parsed.comment shouldBe "" - } +class FitsHeaderCardParserTest { + + @Test + fun shouldParseBooleanValue() { + val parsed = FitsHeaderCardParser("SIMPLE = T / Boolean Type ") + + parsed.key shouldBe "SIMPLE" + parsed.value shouldBe "T" + parsed.type shouldBe FitsHeaderCardType.BOOLEAN + parsed.comment shouldBe "Boolean Type" + } + + @Test + fun shouldParseIntegerValue() { + val parsed = FitsHeaderCardParser("NAXIS = 3 / Integer Type ") + + parsed.key shouldBe "NAXIS" + parsed.value shouldBe "3" + parsed.type shouldBe FitsHeaderCardType.INTEGER + parsed.comment shouldBe "Integer Type" + } + + @Test + fun shouldParseDecimalValue() { + val parsed = FitsHeaderCardParser("EXPOSURE= 150.0 / Decimal Type ") + + parsed.key shouldBe "EXPOSURE" + parsed.value shouldBe "150.0" + parsed.type shouldBe FitsHeaderCardType.DECIMAL + parsed.comment shouldBe "Decimal Type" + } + + @Test + fun shouldParseTextValue() { + val parsed = FitsHeaderCardParser("INSTRUME= 'Camera ' / Text Type ") + + parsed.key shouldBe "INSTRUME" + parsed.value shouldBe "Camera" + parsed.type shouldBe FitsHeaderCardType.TEXT + parsed.comment shouldBe "Text Type" + } + + @Test + fun shouldParseEndKey() { + val parsed = FitsHeaderCardParser("END") + + parsed.key shouldBe "END" + parsed.value shouldBe "" + parsed.type shouldBe FitsHeaderCardType.NONE + parsed.comment shouldBe "" } } diff --git a/nebulosa-fits/src/test/kotlin/FitsReadTest.kt b/nebulosa-fits/src/test/kotlin/FitsReadTest.kt index ca4e1d328..cac55b4e6 100644 --- a/nebulosa-fits/src/test/kotlin/FitsReadTest.kt +++ b/nebulosa-fits/src/test/kotlin/FitsReadTest.kt @@ -4,80 +4,98 @@ import nebulosa.fits.Bitpix import nebulosa.fits.bitpix import nebulosa.fits.fits import nebulosa.image.format.ImageHdu -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.* +import org.junit.jupiter.api.Test -class FitsReadTest : AbstractFitsAndXisfTest() { +class FitsReadTest { - init { - "mono:8-bit" { - val hdu = closeAfterEach(NGC3344_MONO_8_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 1 - hdu.header.bitpix shouldBe Bitpix.BYTE - } - "mono:16-bit" { - val hdu = closeAfterEach(NGC3344_MONO_16_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 1 - hdu.header.bitpix shouldBe Bitpix.SHORT - } - "mono:32-bit" { - val hdu = closeAfterEach(NGC3344_MONO_32_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 1 - hdu.header.bitpix shouldBe Bitpix.INTEGER - } - "mono:32-bit floating-point" { - val hdu = closeAfterEach(NGC3344_MONO_F32_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 1 - hdu.header.bitpix shouldBe Bitpix.FLOAT - } - "mono:64-bit floating-point" { - val hdu = closeAfterEach(NGC3344_MONO_F64_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 1 - hdu.header.bitpix shouldBe Bitpix.DOUBLE - } - "color:8-bit" { - val hdu = closeAfterEach(NGC3344_COLOR_8_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 4 - hdu.header.bitpix shouldBe Bitpix.BYTE - } - "color:16-bit" { - val hdu = closeAfterEach(NGC3344_COLOR_16_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 4 - hdu.header.bitpix shouldBe Bitpix.SHORT - } - "color:32-bit" { - val hdu = closeAfterEach(NGC3344_COLOR_32_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 4 - hdu.header.bitpix shouldBe Bitpix.INTEGER - } - "color:32-bit floating-point" { - val hdu = closeAfterEach(NGC3344_COLOR_F32_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 4 - hdu.header.bitpix shouldBe Bitpix.FLOAT - } - "color:64-bit floating-point" { - val hdu = closeAfterEach(NGC3344_COLOR_F64_FITS.fits()).filterIsInstance().first() - hdu.width shouldBeExactly 256 - hdu.height shouldBeExactly 256 - hdu.numberOfChannels shouldBeExactly 4 - hdu.header.bitpix shouldBe Bitpix.DOUBLE - } + @Test + fun mono8Bit() { + val hdu = NGC3344_MONO_8_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 1 + hdu.header.bitpix shouldBe Bitpix.BYTE + } + + @Test + fun mono16Bit() { + val hdu = NGC3344_MONO_16_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 1 + hdu.header.bitpix shouldBe Bitpix.SHORT + } + + @Test + fun mono32Bit() { + val hdu = NGC3344_MONO_32_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 1 + hdu.header.bitpix shouldBe Bitpix.INTEGER + } + + @Test + fun monoFloat32Bit() { + val hdu = NGC3344_MONO_F32_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 1 + hdu.header.bitpix shouldBe Bitpix.FLOAT + } + + @Test + fun monoFloat64Bit() { + val hdu = NGC3344_MONO_F64_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 1 + hdu.header.bitpix shouldBe Bitpix.DOUBLE + } + + @Test + fun color8Bit() { + val hdu = NGC3344_COLOR_8_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 4 + hdu.header.bitpix shouldBe Bitpix.BYTE + } + + @Test + fun color16Bit() { + val hdu = NGC3344_COLOR_16_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 4 + hdu.header.bitpix shouldBe Bitpix.SHORT + } + + @Test + fun color32Bit() { + val hdu = NGC3344_COLOR_32_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 4 + hdu.header.bitpix shouldBe Bitpix.INTEGER + } + + @Test + fun colorFloat32Bit() { + val hdu = NGC3344_COLOR_F32_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 4 + hdu.header.bitpix shouldBe Bitpix.FLOAT + } + + @Test + fun colorFloat64Bit() { + val hdu = NGC3344_COLOR_F64_FITS.fits().use { it.filterIsInstance().first() } + hdu.width shouldBeExactly 256 + hdu.height shouldBeExactly 256 + hdu.numberOfChannels shouldBeExactly 4 + hdu.header.bitpix shouldBe Bitpix.DOUBLE } } diff --git a/nebulosa-fits/src/test/kotlin/FitsWriteTest.kt b/nebulosa-fits/src/test/kotlin/FitsWriteTest.kt index 3f560ff36..d99ed215b 100644 --- a/nebulosa-fits/src/test/kotlin/FitsWriteTest.kt +++ b/nebulosa-fits/src/test/kotlin/FitsWriteTest.kt @@ -4,22 +4,23 @@ import nebulosa.fits.fits import nebulosa.image.format.ImageHdu import nebulosa.io.sink import nebulosa.io.source -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.AbstractTest +import nebulosa.test.NGC3344_MONO_8_FITS import okio.ByteString.Companion.toByteString +import org.junit.jupiter.api.Test -class FitsWriteTest : AbstractFitsAndXisfTest() { +class FitsWriteTest : AbstractTest() { - init { - "mono" { - val hdu0 = closeAfterEach(NGC3344_MONO_8_FITS.fits()).filterIsInstance().first() - val data = ByteArray(69120) - FitsFormat.write(data.sink(), listOf(hdu0)) - data.toByteString(2880, 66240).md5().hex() shouldBe "e1735e21c94dc49885fabc429406e573" + @Test + fun mono() { + val hdu0 = NGC3344_MONO_8_FITS.fits().autoClose().filterIsInstance().first() + val data = ByteArray(69120) + FitsFormat.write(data.sink(), listOf(hdu0)) + data.toByteString(2880, 66240).md5().hex() shouldBe "e1735e21c94dc49885fabc429406e573" - val fits = data.source().use { it.fits() } - val hdu1 = fits.filterIsInstance().first() + val fits = data.source().use { it.fits() } + val hdu1 = fits.filterIsInstance().first() - hdu0.header shouldBe hdu1.header - } + hdu0.header shouldBe hdu1.header } } diff --git a/nebulosa-hips2fits/src/test/kotlin/Hips2FitsServiceTest.kt b/nebulosa-hips2fits/src/test/kotlin/Hips2FitsServiceTest.kt index ce093b062..4c0b5fd7a 100644 --- a/nebulosa-hips2fits/src/test/kotlin/Hips2FitsServiceTest.kt +++ b/nebulosa-hips2fits/src/test/kotlin/Hips2FitsServiceTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.collections.shouldNotBeEmpty import io.kotest.matchers.doubles.shouldBeExactly @@ -11,31 +9,37 @@ import nebulosa.image.format.ImageHdu import nebulosa.io.source import nebulosa.math.deg import nebulosa.math.toDegrees -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.HTTP_CLIENT +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class Hips2FitsServiceTest : StringSpec() { +@NonGitHubOnly +class Hips2FitsServiceTest { - init { - val service = Hips2FitsService() + @Test + fun query() { + val responseBody = SERVICE + .query("CDS/P/DSS2/red", 201.36506337683.deg, (-43.01911250808).deg) + .execute() + .body() + .shouldNotBeNull() + val fits = responseBody.use { it.bytes().source().fits() } + val hdu = fits.filterIsInstance().first().header + hdu.width shouldBeExactly 1200 + hdu.height shouldBeExactly 900 + hdu.rightAscension.toDegrees shouldBeExactly 201.36506337683 + hdu.declination.toDegrees shouldBeExactly -43.01911250808 + } + + @Test + fun availableSurveys() { + val surveys = SERVICE.availableSurveys().execute().body().shouldNotBeNull() + surveys.shouldNotBeEmpty() + surveys shouldHaveSize 115 + } + + companion object { - "query" { - val responseBody = service - .query("CDS/P/DSS2/red", 201.36506337683.deg, (-43.01911250808).deg) - .execute() - .body() - .shouldNotBeNull() - val fits = responseBody.use { it.bytes().source().fits() } - val hdu = fits.filterIsInstance().first().header - hdu.width shouldBeExactly 1200 - hdu.height shouldBeExactly 900 - hdu.rightAscension.toDegrees shouldBeExactly 201.36506337683 - hdu.declination.toDegrees shouldBeExactly -43.01911250808 - } - "available surveys" { - val surveys = service.availableSurveys().execute().body().shouldNotBeNull() - surveys.shouldNotBeEmpty() - surveys shouldHaveSize 115 - } + @JvmStatic private val SERVICE = Hips2FitsService(httpClient = HTTP_CLIENT) } } diff --git a/nebulosa-horizons/src/test/kotlin/HorizonsServiceTest.kt b/nebulosa-horizons/src/test/kotlin/HorizonsServiceTest.kt index 836dae134..66e890bb1 100644 --- a/nebulosa-horizons/src/test/kotlin/HorizonsServiceTest.kt +++ b/nebulosa-horizons/src/test/kotlin/HorizonsServiceTest.kt @@ -1,5 +1,4 @@ import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.collections.shouldNotBeEmpty import io.kotest.matchers.ints.shouldBeExactly @@ -16,110 +15,118 @@ import nebulosa.math.km import nebulosa.math.m import nebulosa.nasa.daf.SourceDaf import nebulosa.nasa.spk.Spk -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor +import nebulosa.test.HTTP_CLIENT import okio.ByteString.Companion.decodeBase64 +import org.junit.jupiter.api.Test import java.time.LocalDateTime -import java.util.concurrent.TimeUnit - -class HorizonsServiceTest : StringSpec() { - - init { - val httpClient = OkHttpClient.Builder() - .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) - .callTimeout(1, TimeUnit.MINUTES) - .connectTimeout(1, TimeUnit.MINUTES) - .readTimeout(1, TimeUnit.MINUTES) - .build() - - val service = HorizonsService(httpClient = httpClient) - - fun observe( - command: String, - startDate: LocalDateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0), - ) = service - .observer( - command, - 138.73119026648095.deg, 35.36276754848444.deg, 3776.m, - startDate, startDate.plusDays(1L), + +class HorizonsServiceTest { + + private fun observe( + command: String, + startDate: LocalDateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0), + ) = SERVICE.observer( + command, 138.73119026648095.deg, 35.36276754848444.deg, 3776.m, + startDate, startDate.plusDays(1L), extraPrecision = true, + ).execute().body().shouldNotBeNull().also { it.shouldNotBeEmpty() } + + @Test + fun spk() { + val start = LocalDateTime.of(2023, 1, 1, 0, 0) + val end = LocalDateTime.of(2023, 12, 31, 23, 59) + val spkFile = SERVICE.spk(1003517, start, end).execute().body().shouldNotBeNull() + spkFile.id shouldBeExactly 1003517 + val spkBytes = spkFile.spk.decodeBase64() + val spk = Spk(SourceDaf(spkBytes!!.asByteBuffer().source())) + spk.shouldHaveSize(1) + spk[10, 1003517].shouldNotBeNull() + } + + @Test + fun observerSun() { + val ephemeris = observe("10") + val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "274.11210" + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "-23.38427" + } + + @Test + fun observerMoon() { + val ephemeris = observe("301") + val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "313.69977" + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "-23.34363" + } + + @Test + fun observerMars() { + val ephemeris = observe("499") + val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "67.87667" + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "24.65746" + ephemeris[dateTime]!![HorizonsQuantity.CONSTELLATION] shouldBe "Tau" + ephemeris[dateTime]!![HorizonsQuantity.ILLUMINATED_FRACTION] shouldBe "98.32712" + ephemeris[dateTime]!![HorizonsQuantity.VISUAL_MAGNITUDE] shouldBe "-1.426" + ephemeris[dateTime]!![HorizonsQuantity.SURFACE_BRIGHTNESS] shouldBe "4.239" + ephemeris[dateTime]!![HorizonsQuantity.APPARENT_AZ] shouldStartWith "317.88982" + ephemeris[dateTime]!![HorizonsQuantity.APPARENT_ALT] shouldStartWith "-16.31968" + ephemeris[dateTime]!![HorizonsQuantity.APPARENT_HOUR_ANGLE] shouldStartWith "8.99305" + } + + @Test + fun observerCeresBySpkId() { + val ephemeris = observe("DES=2000001;") + val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "185.92892" + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "9.90348" + } + + @Test + fun observerCeresByIauNumber() { + val ephemeris = observe("1;") + val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "185.92892" + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "9.90348" + } + + @Test + fun observerOsculatingElements() { + val ephemeris = SERVICE + .observerWithOsculationElements( + "(2023 GA2)", "2460049.5", ".6183399929327511", + "30.04427847488657", "30.56835826458952", "19.84449491210952", + ".3107780828530178", "2459989.479453452084", + longitude = 314.4173.deg, latitude = (-22.5354318).deg, elevation = 1.81754.km, + startTime = LocalDateTime.of(2023, 3, 11, 0, 0, 0), + endTime = LocalDateTime.of(2023, 4, 11, 0, 0, 0), extraPrecision = true, + stepSizeInMinutes = 1, ).execute().body().shouldNotBeNull() - .also { it.shouldNotBeEmpty() } - - "spk" { - val start = LocalDateTime.of(2023, 1, 1, 0, 0) - val end = LocalDateTime.of(2023, 12, 31, 23, 59) - val spkFile = service.spk(1003517, start, end).execute().body().shouldNotBeNull() - spkFile.id shouldBeExactly 1003517 - val spkBytes = spkFile.spk.decodeBase64() - val spk = Spk(SourceDaf(spkBytes!!.asByteBuffer().source())) - spk.shouldHaveSize(1) - spk[10, 1003517].shouldNotBeNull() - } - "observer: sun" { - val ephemeris = observe("10") - val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "274.11210" - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "-23.38427" - } - "observer: moon" { - val ephemeris = observe("301") - val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "313.69977" - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "-23.34363" - } - "observer: mars" { - val ephemeris = observe("499") - val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "67.87667" - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "24.65746" - ephemeris[dateTime]!![HorizonsQuantity.CONSTELLATION] shouldBe "Tau" - ephemeris[dateTime]!![HorizonsQuantity.ILLUMINATED_FRACTION] shouldBe "98.32712" - ephemeris[dateTime]!![HorizonsQuantity.VISUAL_MAGNITUDE] shouldBe "-1.426" - ephemeris[dateTime]!![HorizonsQuantity.SURFACE_BRIGHTNESS] shouldBe "4.239" - ephemeris[dateTime]!![HorizonsQuantity.APPARENT_AZ] shouldStartWith "317.88982" - ephemeris[dateTime]!![HorizonsQuantity.APPARENT_ALT] shouldStartWith "-16.31968" - ephemeris[dateTime]!![HorizonsQuantity.APPARENT_HOUR_ANGLE] shouldStartWith "8.99305" - } - "observer: ceres by SPK ID" { - val ephemeris = observe("DES=2000001;") - val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "185.92892" - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "9.90348" - } - "observer: ceres by IAU Number" { - val ephemeris = observe("1;") - val dateTime = LocalDateTime.of(2022, 12, 25, 22, 0, 0) - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "185.92892" - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "9.90348" - } - "observer: osculating elements" { - val ephemeris = service - .observerWithOsculationElements( - "(2023 GA2)", "2460049.5", ".6183399929327511", - "30.04427847488657", "30.56835826458952", "19.84449491210952", - ".3107780828530178", "2459989.479453452084", - longitude = 314.4173.deg, latitude = (-22.5354318).deg, elevation = 1.81754.km, - startTime = LocalDateTime.of(2023, 3, 11, 0, 0, 0), - endTime = LocalDateTime.of(2023, 4, 11, 0, 0, 0), - extraPrecision = true, - stepSizeInMinutes = 1, - ).execute().body().shouldNotBeNull() - - ephemeris.shouldNotBeEmpty() - - val dateTime = LocalDateTime.of(2023, 3, 11, 0, 0, 0) - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "344.45591" - ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "14.43086" - } - "cap & nofrag" { - observe("DES=1000041;CAP;NOFRAG", LocalDateTime.now().minusDays(2L)) - } - "non unique object" { - shouldThrow { observe("DES=1000041;") }.recordItems.shouldNotBeEmpty() - } - "no matches found" { - shouldThrow { observe("DES=1;CAP;NOFRAG") } - } + + ephemeris.shouldNotBeEmpty() + + val dateTime = LocalDateTime.of(2023, 3, 11, 0, 0, 0) + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_RA] shouldStartWith "344.45591" + ephemeris[dateTime]!![HorizonsQuantity.ASTROMETRIC_DEC] shouldStartWith "14.43086" + } + + @Test + fun capAndNofrag() { + observe("DES=1000041;CAP;NOFRAG", LocalDateTime.now().minusDays(2L)) + } + + @Test + fun nonUniqueObject() { + shouldThrow { observe("DES=1000041;") }.recordItems.shouldNotBeEmpty() + } + + @Test + fun noMatchesFound() { + shouldThrow { observe("DES=1;CAP;NOFRAG") } + } + + companion object { + + @JvmStatic private val SERVICE = HorizonsService(httpClient = HTTP_CLIENT) } } diff --git a/nebulosa-image-format/src/main/kotlin/nebulosa/image/format/ImageHdu.kt b/nebulosa-image-format/src/main/kotlin/nebulosa/image/format/ImageHdu.kt index 140125c46..3c8275ddf 100644 --- a/nebulosa-image-format/src/main/kotlin/nebulosa/image/format/ImageHdu.kt +++ b/nebulosa-image-format/src/main/kotlin/nebulosa/image/format/ImageHdu.kt @@ -1,5 +1,11 @@ package nebulosa.image.format +import java.awt.image.BufferedImage +import java.awt.image.BufferedImage.TYPE_BYTE_GRAY +import java.awt.image.BufferedImage.TYPE_INT_RGB +import java.awt.image.DataBufferByte +import java.awt.image.DataBufferInt + interface ImageHdu : Hdu { val width: Int @@ -10,4 +16,33 @@ interface ImageHdu : Hdu { val isMono get() = numberOfChannels == 1 + + companion object { + + @JvmStatic + fun ImageHdu.makeImage(): BufferedImage { + val type = if (numberOfChannels == 1) TYPE_BYTE_GRAY else TYPE_INT_RGB + val image = BufferedImage(width, height, type) + val numberOfPixels = data.numberOfPixels + + if (numberOfChannels == 1) { + val buffer = (image.raster.dataBuffer as DataBufferByte).data + + repeat(numberOfPixels) { + buffer[it] = (data.red[it] * 255f).toInt().toByte() + } + } else { + val buffer = (image.raster.dataBuffer as DataBufferInt).data + + repeat(numberOfPixels) { + val red = (data.red[it] * 255f).toInt() and 0xFF + val green = (data.green[it] * 255f).toInt() and 0xFF + val blue = (data.blue[it] * 255f).toInt() and 0xFF + buffer[it] = blue or (green shl 8) or (red shl 16) + } + } + + return image + } + } } diff --git a/nebulosa-image/src/main/kotlin/nebulosa/image/Image.kt b/nebulosa-image/src/main/kotlin/nebulosa/image/Image.kt index eae28c6ec..600d4eb58 100644 --- a/nebulosa-image/src/main/kotlin/nebulosa/image/Image.kt +++ b/nebulosa-image/src/main/kotlin/nebulosa/image/Image.kt @@ -221,6 +221,9 @@ class Image internal constructor( companion object { + @JvmStatic + fun T.asImage(debayer: Boolean = true) where T : ImageRepresentation, T : AutoCloseable = use { open(it, debayer) } + @JvmStatic internal fun colorModel(mono: Boolean): ColorModel { val space = ColorSpace.getInstance(if (mono) ColorSpace.CS_GRAY else ColorSpace.CS_sRGB) diff --git a/nebulosa-image/src/test/kotlin/ComputationAlgorithmTest.kt b/nebulosa-image/src/test/kotlin/ComputationAlgorithmTest.kt index 1ade07c95..a1d194c96 100644 --- a/nebulosa-image/src/test/kotlin/ComputationAlgorithmTest.kt +++ b/nebulosa-image/src/test/kotlin/ComputationAlgorithmTest.kt @@ -3,87 +3,94 @@ import io.kotest.matchers.floats.shouldBeExactly import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.fits.fits -import nebulosa.image.Image +import nebulosa.image.Image.Companion.asImage import nebulosa.image.algorithms.computation.MedianAbsoluteDeviation import nebulosa.image.algorithms.computation.Statistics import nebulosa.image.format.ImageChannel -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.NGC3344_COLOR_F32_FITS +import nebulosa.test.NGC3344_MONO_F32_FITS +import org.junit.jupiter.api.Test -class ComputationAlgorithmTest : AbstractFitsAndXisfTest() { +class ComputationAlgorithmTest { - init { - "mono:median absolute deviation" { - val mImage = Image.open(NGC3344_MONO_F32_FITS.fits()) - mImage.compute(MedianAbsoluteDeviation()) shouldBe (0.0862f plusOrMinus 1e-4f) - } - "mono:statistics" { - val mImage = Image.open(NGC3344_MONO_F32_FITS.fits()) - val statistics = mImage.compute(Statistics.GRAY) + @Test + fun monoMedianAbsoluteDeviation() { + val mImage = NGC3344_MONO_F32_FITS.fits().asImage() + mImage.compute(MedianAbsoluteDeviation()) shouldBe (0.0862f plusOrMinus 1e-4f) + } + + @Test + fun monoStatistics() { + val mImage = NGC3344_MONO_F32_FITS.fits().asImage() + val statistics = mImage.compute(Statistics.GRAY) + + statistics.count shouldBeExactly 65536 + statistics.maxCount shouldBeExactly 926 + statistics.mean shouldBe (0.2848f plusOrMinus 1e-4f) + statistics.sumOfSquares shouldBe (6827.543f plusOrMinus 1e-3f) + statistics.median shouldBe (0.2470f plusOrMinus 1e-4f) + statistics.variance shouldBe (0.02302f plusOrMinus 1e-4f) + statistics.stdDev shouldBe (0.1517f plusOrMinus 1e-4f) + statistics.avgDev shouldBe (0.11553f plusOrMinus 1e-5f) + statistics.minimum shouldBeExactly 0.03529412f + statistics.maximum shouldBeExactly 1f + } + + @Test + fun colorMedianAbsoluteDeviation() { + val cImage = NGC3344_COLOR_F32_FITS.fits().asImage() + cImage.compute(MedianAbsoluteDeviation(channel = ImageChannel.RED)) shouldBe (0.0823f plusOrMinus 1e-4f) + cImage.compute(MedianAbsoluteDeviation(channel = ImageChannel.GREEN)) shouldBe (0.0745f plusOrMinus 1e-4f) + cImage.compute(MedianAbsoluteDeviation(channel = ImageChannel.BLUE)) shouldBe (0.0705f plusOrMinus 1e-4f) + } + + @Test + fun colorStatistics() { + val cImage = NGC3344_COLOR_F32_FITS.fits().asImage() + + run { + val statistics = cImage.compute(Statistics.RED) statistics.count shouldBeExactly 65536 - statistics.maxCount shouldBeExactly 926 - statistics.mean shouldBe (0.2848f plusOrMinus 1e-4f) - statistics.sumOfSquares shouldBe (6827.543f plusOrMinus 1e-3f) + statistics.maxCount shouldBeExactly 1027 + statistics.mean shouldBe (0.2843f plusOrMinus 1e-4f) + statistics.sumOfSquares shouldBe (6723.677f plusOrMinus 1e-3f) statistics.median shouldBe (0.2470f plusOrMinus 1e-4f) - statistics.variance shouldBe (0.02302f plusOrMinus 1e-4f) - statistics.stdDev shouldBe (0.1517f plusOrMinus 1e-4f) - statistics.avgDev shouldBe (0.11553f plusOrMinus 1e-5f) - statistics.minimum shouldBeExactly 0.03529412f + statistics.variance shouldBe (0.0217f plusOrMinus 1e-4f) + statistics.stdDev shouldBe (0.1474f plusOrMinus 1e-4f) + statistics.avgDev shouldBe (0.11034f plusOrMinus 1e-5f) + statistics.minimum shouldBeExactly 0.047058824f statistics.maximum shouldBeExactly 1f } - "color:median absolute deviation" { - val cImage = Image.open(NGC3344_COLOR_F32_FITS.fits()) - cImage.compute(MedianAbsoluteDeviation(channel = ImageChannel.RED)) shouldBe (0.0823f plusOrMinus 1e-4f) - cImage.compute(MedianAbsoluteDeviation(channel = ImageChannel.GREEN)) shouldBe (0.0745f plusOrMinus 1e-4f) - cImage.compute(MedianAbsoluteDeviation(channel = ImageChannel.BLUE)) shouldBe (0.0705f plusOrMinus 1e-4f) - } - "color:statistics" { - val cImage = Image.open(NGC3344_COLOR_F32_FITS.fits()) - - run { - val statistics = cImage.compute(Statistics.RED) - statistics.count shouldBeExactly 65536 - statistics.maxCount shouldBeExactly 1027 - statistics.mean shouldBe (0.2843f plusOrMinus 1e-4f) - statistics.sumOfSquares shouldBe (6723.677f plusOrMinus 1e-3f) - statistics.median shouldBe (0.2470f plusOrMinus 1e-4f) - statistics.variance shouldBe (0.0217f plusOrMinus 1e-4f) - statistics.stdDev shouldBe (0.1474f plusOrMinus 1e-4f) - statistics.avgDev shouldBe (0.11034f plusOrMinus 1e-5f) - statistics.minimum shouldBeExactly 0.047058824f - statistics.maximum shouldBeExactly 1f - } + run { + val statistics = cImage.compute(Statistics.GREEN) - run { - val statistics = cImage.compute(Statistics.GREEN) - - statistics.count shouldBeExactly 65536 - statistics.maxCount shouldBeExactly 1181 - statistics.mean shouldBe (0.2635f plusOrMinus 1e-4f) - statistics.sumOfSquares shouldBe (5845.3496f plusOrMinus 1e-3f) - statistics.median shouldBe (0.2235f plusOrMinus 1e-4f) - statistics.variance shouldBe (0.0197f plusOrMinus 1e-4f) - statistics.stdDev shouldBe (0.1404f plusOrMinus 1e-4f) - statistics.avgDev shouldBe (0.10392f plusOrMinus 1e-5f) - statistics.minimum shouldBeExactly 0.050980393f - statistics.maximum shouldBeExactly 1f - } + statistics.count shouldBeExactly 65536 + statistics.maxCount shouldBeExactly 1181 + statistics.mean shouldBe (0.2635f plusOrMinus 1e-4f) + statistics.sumOfSquares shouldBe (5845.3496f plusOrMinus 1e-3f) + statistics.median shouldBe (0.2235f plusOrMinus 1e-4f) + statistics.variance shouldBe (0.0197f plusOrMinus 1e-4f) + statistics.stdDev shouldBe (0.1404f plusOrMinus 1e-4f) + statistics.avgDev shouldBe (0.10392f plusOrMinus 1e-5f) + statistics.minimum shouldBeExactly 0.050980393f + statistics.maximum shouldBeExactly 1f + } - run { - val statistics = cImage.compute(Statistics.BLUE) + run { + val statistics = cImage.compute(Statistics.BLUE) - statistics.count shouldBeExactly 65536 - statistics.maxCount shouldBeExactly 1234 - statistics.mean shouldBe (0.2619f plusOrMinus 1e-4f) - statistics.sumOfSquares shouldBe (5615.849f plusOrMinus 1e-3f) - statistics.median shouldBe (0.2235f plusOrMinus 1e-4f) - statistics.variance shouldBe (0.0170f plusOrMinus 1e-4f) - statistics.stdDev shouldBe (0.1306f plusOrMinus 1e-4f) - statistics.avgDev shouldBe (0.09864f plusOrMinus 1e-5f) - statistics.minimum shouldBeExactly 0.050980393f - statistics.maximum shouldBeExactly 1f - } + statistics.count shouldBeExactly 65536 + statistics.maxCount shouldBeExactly 1234 + statistics.mean shouldBe (0.2619f plusOrMinus 1e-4f) + statistics.sumOfSquares shouldBe (5615.849f plusOrMinus 1e-3f) + statistics.median shouldBe (0.2235f plusOrMinus 1e-4f) + statistics.variance shouldBe (0.0170f plusOrMinus 1e-4f) + statistics.stdDev shouldBe (0.1306f plusOrMinus 1e-4f) + statistics.avgDev shouldBe (0.09864f plusOrMinus 1e-5f) + statistics.minimum shouldBeExactly 0.050980393f + statistics.maximum shouldBeExactly 1f } } } diff --git a/nebulosa-image/src/test/kotlin/FitsTransformAlgorithmTest.kt b/nebulosa-image/src/test/kotlin/FitsTransformAlgorithmTest.kt index 631ae94c8..fba695c05 100644 --- a/nebulosa-image/src/test/kotlin/FitsTransformAlgorithmTest.kt +++ b/nebulosa-image/src/test/kotlin/FitsTransformAlgorithmTest.kt @@ -3,276 +3,383 @@ import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.fits.fits -import nebulosa.image.Image +import nebulosa.image.Image.Companion.asImage import nebulosa.image.algorithms.transformation.* import nebulosa.image.algorithms.transformation.convolution.* import nebulosa.image.format.ImageChannel -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.DEBAYER_FITS +import nebulosa.test.NGC3344_COLOR_32_FITS +import nebulosa.test.NGC3344_MONO_8_FITS +import nebulosa.test.save +import org.junit.jupiter.api.Test -class FitsTransformAlgorithmTest : AbstractFitsAndXisfTest() { +class FitsTransformAlgorithmTest { init { - "mono:raw" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + @Test + fun monoRaw() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.save("fits-mono-raw").second shouldBe "e17cfc29c3b343409cd8617b6913330e" } - "mono:vertical flip" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoVerticalFlip() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(VerticalFlip) mImage.save("fits-mono-vertical-flip").second shouldBe "262260dfe719726c0e7829a088279a21" } - "mono:horizontal flip" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoHorizontalFlip() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(HorizontalFlip) mImage.save("fits-mono-horizontal-flip").second shouldBe "daf0f05db5de3750962f338527564b27" } - "mono:vertical & horizontal flip" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoVerticalAndHorizontalFlip() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(VerticalFlip, HorizontalFlip) mImage.save("fits-mono-vertical-horizontal-flip").second shouldBe "3bc81f579a0e34ce9312c3b242209166" } - "mono:subframe" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoSubframe() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) nImage.width shouldBeExactly 16 nImage.height shouldBeExactly 16 nImage.mono.shouldBeTrue() nImage.save("fits-mono-subframe").second shouldBe "4d9984e778f82dde10b9aeeee7a29fe0" } - "mono:sharpen" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoSharpen() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(Sharpen) mImage.save("fits-mono-sharpen").second shouldBe "0b162242a4e673f6480b5206cf49ca50" } - "mono:mean" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoMean() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(Mean) mImage.save("fits-mono-mean").second shouldBe "cf866292f657c379ae3965931dd8eeea" } - "mono:invert" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoInvert() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(Invert) mImage.save("fits-mono-invert").second shouldBe "6e94463bb5b9561de1f0ee0a154db53e" } - "mono:emboss" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoEmboss() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(Emboss) mImage.save("fits-mono-emboss").second shouldBe "94a8ef5e4573e392d087cf10c905ba12" } - "mono:edges" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoEdges() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(Edges) mImage.save("fits-mono-edges").second shouldBe "27ccd5f5e6098d0cae27e7495e18dd72" } - "mono:blur" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoBlur() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(Blur) mImage.save("fits-mono-blur").second shouldBe "f2c5466dccf71b5c4bee86c5fbbb95fc" } - "mono:gaussian blur" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoGaussianBlur() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) mImage.save("fits-mono-gaussian-blur").second shouldBe "69057b0c4461fb0d55b779da9e72fd69" } - "mono:STF:midtone = 0.1, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoStfMidtone01Shadow00Highlight10() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.1f)) mImage.save("fits-mono-stf-01-00-10").second shouldBe "22c0bd985e70a01330722d912869d6ee" } - "mono:STF:midtone = 0.9, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoStfMidtone09Shadow00Highlight10() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.9f)) mImage.save("fits-mono-stf-09-00-10").second shouldBe "553ccb7546dce3a8f742d5e8f7c58a3f" } - "mono:STF:midtone = 0.1, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoStfMidtone01Shadow05Highlight10() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) mImage.save("fits-mono-stf-01-05-10").second shouldBe "f31db854fab72033dce2f8c572ec6783" } - "mono:STF:midtone = 0.9, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoStfMidtone09Shadow05Highlight10() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) mImage.save("fits-mono-stf-09-05-10").second shouldBe "633b49c4a1dbb5ad8e6a9d74f330636d" } - "mono:STF:midtone = 0.1, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoStfMidtone01Shadow00Highlight05() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) mImage.save("fits-mono-stf-01-00-05").second shouldBe "26036937eb3e5f99cd6129f709ce4b31" } - "mono:STF:midtone = 0.9, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoStfMidtone09Shadow00Highlight05() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) mImage.save("fits-mono-stf-09-00-05").second shouldBe "e8f694dae666ac15ce2f8a169eb84024" } - "mono:STF:midtone = 0.1, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoStfMidtone01Shadow04Highlight06() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) mImage.save("fits-mono-stf-01-04-06").second shouldBe "5226aba21669a24f985703b3e7220568" } - "mono:STF:midtone = 0.9, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoStfMidtone09Shadow04Highlight06() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) mImage.save("fits-mono-stf-09-04-06").second shouldBe "c2acb25ef7be92a51f63e673ec9a850f" } - "mono:auto STF" { - val mImage = Image.open(NGC3344_MONO_8_FITS.fits()) + + @Test + fun monoAutoStf() { + val mImage = NGC3344_MONO_8_FITS.fits().asImage() mImage.transform(AutoScreenTransformFunction) mImage.save("fits-mono-auto-stf").second shouldBe "e17cfc29c3b343409cd8617b6913330e" } - "color:raw" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorRaw() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.save("fits-color-raw").second shouldBe "18fb83e240bc7a4cbafbc1aba2741db6" } - "color:vertical flip" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorVerticalFlip() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(VerticalFlip) mImage.save("fits-color-vertical-flip").second shouldBe "b717ecda5c5bba50cfa06304ef2bca88" } - "color:horizontal flip" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorHorizontalFlip() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(HorizontalFlip) mImage.save("fits-color-horizontal-flip").second shouldBe "f70228600c77551473008ed4b9986439" } - "color:vertical & horizontal flip" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorVerticalAndHorizontalFlip() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(VerticalFlip, HorizontalFlip) mImage.save("fits-color-vertical-horizontal-flip").second shouldBe "1237314044f20307b76203148af855e3" } - "color:subframe" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorSubframe() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) nImage.width shouldBeExactly 16 nImage.height shouldBeExactly 16 nImage.mono.shouldBeFalse() nImage.save("fits-color-subframe").second shouldBe "282fc4fdf9142fcb4b18e1df1eef4caa" } - "color:sharpen" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorSharpen() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(Sharpen) mImage.save("fits-color-sharpen").second shouldBe "e562282bdafdeba6ce88981bb9c3ba61" } - "color:mean" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorMean() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(Mean) mImage.save("fits-color-mean").second shouldBe "a8380d928aaa756e202ba43bd3a2f207" } - "color:invert" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorInvert() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(Invert) mImage.save("fits-color-invert").second shouldBe "decad269ec26450aebeaf7546867b5f8" } - "color:emboss" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorEmboss() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(Emboss) mImage.save("fits-color-emboss").second shouldBe "58d69250f1233055aa33f9ec7ca40af1" } - "color:edges" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorEdges() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(Edges) mImage.save("fits-color-edges").second shouldBe "091f2955740a8edcd2401dc416d19d51" } - "color:blur" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorBlur() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(Blur) mImage.save("fits-color-blur").second shouldBe "0fca440b763de5380fa29de736f3c792" } - "color:gaussian blur" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorGaussianBlur() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) mImage.save("fits-color-gaussian-blur").second shouldBe "394d1a4f136f15c802dd73004c421d64" } - "color:STF:midtone = 0.1, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorStfMidtone01Shadow00Highlight10() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.1f)) mImage.save("fits-color-stf-01-00-10").second shouldBe "e952bd263df6fd275b9a80aca554cb4b" } - "color:STF:midtone = 0.9, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorStfMidtone09Shadow00Highlight10() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.9f)) mImage.save("fits-color-stf-09-00-10").second shouldBe "038809d7612018e2e5c19d5e1f551abd" } - "color:STF:midtone = 0.1, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorStfMidtone01Shadow05Highlight10() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) mImage.save("fits-color-stf-01-05-10").second shouldBe "70e812260f56f8621002327575611f31" } - "color:STF:midtone = 0.9, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorStfMidtone09Shadow05Highlight10() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) mImage.save("fits-color-stf-09-05-10").second shouldBe "6ca400f617f466a9eb02a3a6f2985d99" } - "color:STF:midtone = 0.1, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorStfMidtone01Shadow00Highlight05() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) mImage.save("fits-color-stf-01-00-05").second shouldBe "3cd98ee9a8949d5100295acccd77010b" } - "color:STF:midtone = 0.9, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorStfMidtone09Shadow00Highlight05() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) mImage.save("fits-color-stf-09-00-05").second shouldBe "2cfeffc88c893cc5883d8a2221f29b91" } - "color:STF:midtone = 0.1, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorStfMidtone01Shadow04Highlight06() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) mImage.save("fits-color-stf-01-04-06").second shouldBe "532a07a1a166eb007c2e40651aec2097" } - "color:STF:midtone = 0.9, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorStfMidtone09Shadow04Highlight06() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) mImage.save("fits-color-stf-09-04-06").second shouldBe "eb3d940d9fd2c8814e930715e89897c4" } - "color:auto STF" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorAutoStf() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(AutoScreenTransformFunction) mImage.save("fits-color-auto-stf").second shouldBe "a9c3657d8597b927607eb438e666d3a0" } - "color:SCNR Maximum Mask" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorScnrMaximumMask() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_MASK)) mImage.save("fits-color-scnr-maximum-mask").second shouldBe "e7d2155e18ff1e3172f4e849ae983145" } - "color:SCNR Additive Mask" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorScnrAdditiveMask() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.ADDITIVE_MASK)) mImage.save("fits-color-scnr-additive-mask").second shouldBe "a458c44cedcda704de16d80053fd87eb" } - "color:SCNR Average Neutral" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorScnrAverageNeutral() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.AVERAGE_NEUTRAL)) mImage.save("fits-color-scnr-average-neutral").second shouldBe "e07345ffc4982a62301c95c76d3efb35" } - "color:SCNR Maximum Neutral" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorScnrMaximumNeutral() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_NEUTRAL)) mImage.save("fits-color-scnr-maximum-neutral").second shouldBe "a1d4b04f57b001ba4a996bab0407fd7e" } - "color:SCNR Minimum Neutral" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorScnrMinimumNeutral() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MINIMUM_NEUTRAL)) mImage.save("fits-color-scnr-minimum-neutral").second shouldBe "8b7be57ff38da9c97b35d7888047c0f9" } - "color:grayscale BT-709" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorGrayscaleBt709() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() val nImage = mImage.transform(Grayscale.BT709) nImage.save("fits-color-grayscale-bt709").second shouldBe "cab675aa35390a2d58cd48555d91054f" } - "color:grayscale RMY" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorGrayscaleRmy() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() val nImage = mImage.transform(Grayscale.RMY) nImage.save("fits-color-grayscale-rmy").second shouldBe "e113627002a4178d1010a2f6246e325f" } - "color:grayscale Y" { - val mImage = Image.open(NGC3344_COLOR_32_FITS.fits()) + + @Test + fun colorGrayscaleY() { + val mImage = NGC3344_COLOR_32_FITS.fits().asImage() val nImage = mImage.transform(Grayscale.Y) nImage.save("fits-color-grayscale-y").second shouldBe "24dd4a7e0fa9e4be34c53c924a78a940" } - "color:debayer" { - val mImage = Image.open(DEBAYER_FITS.fits()) + + @Test + fun colorDebayer() { + val mImage = DEBAYER_FITS.fits().asImage() val nImage = mImage.transform(AutoScreenTransformFunction) nImage.save("fits-color-debayer").second shouldBe "86b5bdd67dfd6bbf5495afae4bf2bc04" } - "color:no-debayer" { - val mImage = Image.open(DEBAYER_FITS.fits(), false) + + @Test + fun colorNoDebayer() { + val mImage = DEBAYER_FITS.fits().asImage(false) val nImage = mImage.transform(AutoScreenTransformFunction) nImage.save("fits-color-no-debayer").second shouldBe "958ccea020deec1f0c075042a9ba37c3" } diff --git a/nebulosa-image/src/test/kotlin/HFDTest.kt b/nebulosa-image/src/test/kotlin/HFDTest.kt index c5f4305a8..7092a5536 100644 --- a/nebulosa-image/src/test/kotlin/HFDTest.kt +++ b/nebulosa-image/src/test/kotlin/HFDTest.kt @@ -1,41 +1,41 @@ import io.kotest.matchers.floats.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.fits.fits -import nebulosa.image.Image +import nebulosa.image.Image.Companion.asImage import nebulosa.image.algorithms.computation.hfd.HFD -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.* +import org.junit.jupiter.api.Test -class HFDTest : AbstractFitsAndXisfTest() { +class HFDTest { - init { - "focus" { - val starFocus = arrayOf( - STAR_FOCUS_1 to floatArrayOf(7.0f, 77.2f, 11950.023f), - STAR_FOCUS_2 to floatArrayOf(7.3f, 114.1f, 26055.076f), - STAR_FOCUS_3 to floatArrayOf(7.7f, 185.1f, 68531.1f), - STAR_FOCUS_4 to floatArrayOf(6.9f, 363.4f, 264183.53f), - STAR_FOCUS_5 to floatArrayOf(6.2f, 382.5f, 292700.53f), - STAR_FOCUS_6 to floatArrayOf(6.1f, 410.0f, 336331.9f), - STAR_FOCUS_7 to floatArrayOf(5.7f, 422.7f, 357451.03f), - STAR_FOCUS_8 to floatArrayOf(5.1f, 430.5f, 370737.5f), - STAR_FOCUS_9 to floatArrayOf(4.8f, 435.4f, 379284.03f), - STAR_FOCUS_10 to floatArrayOf(4.3f, 444.6f, 395406.34f), - STAR_FOCUS_11 to floatArrayOf(3.79f, 442.9f, 392338.5f), - STAR_FOCUS_12 to floatArrayOf(3.71f, 443.3f, 393080.38f), - STAR_FOCUS_13 to floatArrayOf(4.9f, 443.6f, 393603.97f), - STAR_FOCUS_14 to floatArrayOf(6.1f, 368.8f, 272057.78f), - STAR_FOCUS_15 to floatArrayOf(6.9f, 249.0f, 124078.05f), - STAR_FOCUS_16 to floatArrayOf(6.5f, 188.2f, 70905.97f), - STAR_FOCUS_17 to floatArrayOf(6.9f, 164.3f, 53994.75f), - ) + @Test + fun focus() { + val starFocus = arrayOf( + STAR_FOCUS_1 to floatArrayOf(7.0f, 77.2f, 11950.023f), + STAR_FOCUS_2 to floatArrayOf(7.3f, 114.1f, 26055.076f), + STAR_FOCUS_3 to floatArrayOf(7.7f, 185.1f, 68531.1f), + STAR_FOCUS_4 to floatArrayOf(6.9f, 363.4f, 264183.53f), + STAR_FOCUS_5 to floatArrayOf(6.2f, 382.5f, 292700.53f), + STAR_FOCUS_6 to floatArrayOf(6.1f, 410.0f, 336331.9f), + STAR_FOCUS_7 to floatArrayOf(5.7f, 422.7f, 357451.03f), + STAR_FOCUS_8 to floatArrayOf(5.1f, 430.5f, 370737.5f), + STAR_FOCUS_9 to floatArrayOf(4.8f, 435.4f, 379284.03f), + STAR_FOCUS_10 to floatArrayOf(4.3f, 444.6f, 395406.34f), + STAR_FOCUS_11 to floatArrayOf(3.79f, 442.9f, 392338.5f), + STAR_FOCUS_12 to floatArrayOf(3.71f, 443.3f, 393080.38f), + STAR_FOCUS_13 to floatArrayOf(4.9f, 443.6f, 393603.97f), + STAR_FOCUS_14 to floatArrayOf(6.1f, 368.8f, 272057.78f), + STAR_FOCUS_15 to floatArrayOf(6.9f, 249.0f, 124078.05f), + STAR_FOCUS_16 to floatArrayOf(6.5f, 188.2f, 70905.97f), + STAR_FOCUS_17 to floatArrayOf(6.9f, 164.3f, 53994.75f), + ) - for ((first, second) in starFocus) { - val focusImage = Image.open(closeAfterEach(first.fits())) - val star = focusImage.compute(HFD(focusImage.width / 2, focusImage.height / 2, 50)) - star.hfd shouldBe (second[0] plusOrMinus 0.1f) - star.snr shouldBe (second[1] plusOrMinus 0.1f) - star.flux shouldBe (second[2] plusOrMinus 0.1f) - } + for ((first, second) in starFocus) { + val focusImage = first.fits().asImage() + val star = focusImage.compute(HFD(focusImage.width / 2, focusImage.height / 2, 50)) + star.hfd shouldBe (second[0] plusOrMinus 0.1f) + star.snr shouldBe (second[1] plusOrMinus 0.1f) + star.flux shouldBe (second[2] plusOrMinus 0.1f) } } } diff --git a/nebulosa-image/src/test/kotlin/XisfTransformAlgorithmTest.kt b/nebulosa-image/src/test/kotlin/XisfTransformAlgorithmTest.kt index d42c91837..3aa5aae07 100644 --- a/nebulosa-image/src/test/kotlin/XisfTransformAlgorithmTest.kt +++ b/nebulosa-image/src/test/kotlin/XisfTransformAlgorithmTest.kt @@ -2,279 +2,387 @@ import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe -import nebulosa.image.Image +import nebulosa.image.Image.Companion.asImage import nebulosa.image.algorithms.transformation.* import nebulosa.image.algorithms.transformation.convolution.* import nebulosa.image.format.ImageChannel -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.DEBAYER_FITS +import nebulosa.test.M82_COLOR_32_XISF +import nebulosa.test.M82_MONO_8_XISF +import nebulosa.test.save import nebulosa.xisf.xisf +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test -class XisfTransformAlgorithmTest : AbstractFitsAndXisfTest() { - - init { - "mono:raw" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.save("xisf-mono-raw").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } - "mono:vertical flip" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(VerticalFlip) - mImage.save("xisf-mono-vertical-flip").second shouldBe "d8250e5ac45d9a6ec04bed2a583a70b2" - } - "mono:horizontal flip" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(HorizontalFlip) - mImage.save("xisf-mono-horizontal-flip").second shouldBe "9e2a05c331a5daeff20671a365e8b74c" - } - "mono:vertical & horizontal flip" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(VerticalFlip, HorizontalFlip) - mImage.save("xisf-mono-vertical-horizontal-flip").second shouldBe "c7d55e870850c10619f389e3f9ec1243" - } - "mono:subframe" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) - nImage.width shouldBeExactly 16 - nImage.height shouldBeExactly 16 - nImage.mono.shouldBeTrue() - nImage.save("xisf-mono-subframe").second shouldBe "dc0ca19c5d40f90e4daac87fbab8f449" - } - "mono:sharpen" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(Sharpen) - mImage.save("xisf-mono-sharpen").second shouldBe "08421f6afef422cdaa9f464a860808ce" - } - "mono:mean" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(Mean) - mImage.save("xisf-mono-mean").second shouldBe "4127027d467ce9537049dc5f25f23ede" - } - "mono:invert" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(Invert) - mImage.save("xisf-mono-invert").second shouldBe "a6ec4a55225cb18f004e159a60137fe9" - } - "mono:emboss" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(Emboss) - mImage.save("xisf-mono-emboss").second shouldBe "2f07b3964b52f6b141b8cc45639b2287" - } - "mono:edges" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(Edges) - mImage.save("xisf-mono-edges").second shouldBe "897af72d536bca57cce77f09e396adb7" - } - "mono:blur" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(Blur) - mImage.save("xisf-mono-blur").second shouldBe "5a850ef20c0ebd461e676523823ff6ea" - } - "mono:gaussian blur" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) - mImage.save("xisf-mono-gaussian-blur").second shouldBe "cb7489cb1948e802ea4d92bba24e0739" - } - "mono:STF:midtone = 0.1, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f)) - mImage.save("xisf-mono-stf-01-00-10").second shouldBe "91621dba5a58081347d156f2152bd8c6" - } - "mono:STF:midtone = 0.9, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f)) - mImage.save("xisf-mono-stf-09-00-10").second shouldBe "9a67728cfc4f871ea8ab6557f381949e" - } - "mono:STF:midtone = 0.1, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) - mImage.save("xisf-mono-stf-01-05-10").second shouldBe "c70b9caaaf88bba9c64891a2b3ba8033" - } - "mono:STF:midtone = 0.9, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) - mImage.save("xisf-mono-stf-09-05-10").second shouldBe "4f4c49b96bd9aa417f22c59b8224ea90" - } - "mono:STF:midtone = 0.1, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) - mImage.save("xisf-mono-stf-01-00-05").second shouldBe "b0c9598e3003d72a5fba617c30dc1821" - } - "mono:STF:midtone = 0.9, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) - mImage.save("xisf-mono-stf-09-00-05").second shouldBe "5824ac8c2d44c2c8ff8121e0904bb77b" - } - "mono:STF:midtone = 0.1, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) - mImage.save("xisf-mono-stf-01-04-06").second shouldBe "20417dfc43fc4c5cb425875614d8066c" - } - "mono:STF:midtone = 0.9, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) - mImage.save("xisf-mono-stf-09-04-06").second shouldBe "13d13ad164a952a29f7ad76e1e51e7d0" - } - "mono:auto STF" { - val mImage = Image.open(M82_MONO_8_XISF.xisf()) - mImage.transform(AutoScreenTransformFunction) - mImage.save("xisf-mono-auto-stf").second shouldBe "9204a71df3770e8fe5ca49e3420eed72" - } - "color:raw" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.save("xisf-color-raw").second shouldBe "764e326cc5260d81f3761112ad6a1969" - } - "color:vertical flip" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(VerticalFlip) - mImage.save("xisf-color-vertical-flip").second shouldBe "595d8d3e46e91d7adf7591e591a7a98d" - } - "color:horizontal flip" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(HorizontalFlip) - mImage.save("xisf-color-horizontal-flip").second shouldBe "b3d9609ef88db9b9215b4e21fdd4992a" - } - "color:vertical & horizontal flip" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(VerticalFlip, HorizontalFlip) - mImage.save("xisf-color-vertical-horizontal-flip").second shouldBe "1c9be1a7e5a66a989f6d0715c7ff30e4" - } - "color:subframe" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) - nImage.width shouldBeExactly 16 - nImage.height shouldBeExactly 16 - nImage.mono.shouldBeFalse() - nImage.save("xisf-color-subframe").second shouldBe "dcf5bc8908ea6a32a5e98f14152f47b9" - } - "color:sharpen" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(Sharpen) - mImage.save("xisf-color-sharpen").second shouldBe "1017a88ff2caeb83a650d94d96c59347" - } - "color:mean" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(Mean) - mImage.save("xisf-color-mean").second shouldBe "eb948cab18442d008381f392bf43542a" - } - "color:invert" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(Invert) - mImage.save("xisf-color-invert").second shouldBe "bbc97cdcde240873439bc29ff1adf169" - } - "color:emboss" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(Emboss) - mImage.save("xisf-color-emboss").second shouldBe "fba6827f204df93f4aaf2f99b113a8f7" - } - "color:edges" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(Edges) - mImage.save("xisf-color-edges").second shouldBe "71d6b5c936ab8165c7c1b63514d2da7b" - } - "color:blur" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(Blur) - mImage.save("xisf-color-blur").second shouldBe "5c9f738a309fc86956d124f0f109eea2" - } - "color:gaussian blur" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) - mImage.save("xisf-color-gaussian-blur").second shouldBe "648beabcea0b486efc3f0c4ded632a06" - } - "color:STF:midtone = 0.1, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f)) - mImage.save("xisf-color-stf-01-00-10").second shouldBe "d84577785e44179f7af7c00807be5260" - } - "color:STF:midtone = 0.9, shadow = 0.0, highlight = 1.0" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f)) - mImage.save("xisf-color-stf-09-00-10").second shouldBe "11771bfe0e180e1efea4568902109013" - } - "color:STF:midtone = 0.1, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) - mImage.save("xisf-color-stf-01-05-10").second shouldBe "4e3b204d8a7ed06dfd05b7d010ef267b" - } - "color:STF:midtone = 0.9, shadow = 0.5, highlight = 1.0" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) - mImage.save("xisf-color-stf-09-05-10").second shouldBe "95c5b801978abdbb51afc9d0de12c218" - } - "color:STF:midtone = 0.1, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) - mImage.save("xisf-color-stf-01-00-05").second shouldBe "08322042c9e57102fec77a06b28c263d" - } - "color:STF:midtone = 0.9, shadow = 0.0, highlight = 0.5" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) - mImage.save("xisf-color-stf-09-00-05").second shouldBe "c99990d3a4bc6fc3aa7a8ac90d452419" - } - "color:STF:midtone = 0.1, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) - mImage.save("xisf-color-stf-01-04-06").second shouldBe "33bce2d7f7cde67d0ee618439f438ee7" - } - "color:STF:midtone = 0.9, shadow = 0.4, highlight = 0.6" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) - mImage.save("xisf-color-stf-09-04-06").second shouldBe "923e56840bcea4462160e36f2e801f5e" - } - "color:auto STF" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(AutoScreenTransformFunction) - mImage.save("xisf-color-auto-stf").second shouldBe "b1460451ad0f0580802d3d6d3a6750ba" - } - "color:SCNR Maximum Mask" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_MASK)) - mImage.save("xisf-color-scnr-maximum-mask").second shouldBe "e465a2fc0814055e089a3180c0235c8b" - } - "color:SCNR Additive Mask" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.ADDITIVE_MASK)) - mImage.save("xisf-color-scnr-additive-mask").second shouldBe "1016ec07253f869097d2d9c67194b3e3" - } - "color:SCNR Average Neutral" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.AVERAGE_NEUTRAL)) - mImage.save("xisf-color-scnr-average-neutral").second shouldBe "2f2583bfb2ae4a9e4441dc9b827196f7" - } - "color:SCNR Maximum Neutral" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_NEUTRAL)) - mImage.save("xisf-color-scnr-maximum-neutral").second shouldBe "dabd995db4fba052617b148b3700378d" - } - "color:SCNR Minimum Neutral" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MINIMUM_NEUTRAL)) - mImage.save("xisf-color-scnr-minimum-neutral").second shouldBe "b92e5130ec2a44c0afd1ca3782261e47" - } - "color:grayscale BT-709" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - val nImage = mImage.transform(Grayscale.BT709) - nImage.save("xisf-color-grayscale-bt709").second shouldBe "bf78482866a0b3a216fa5262f28b0e5e" - } - "color:grayscale RMY" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - val nImage = mImage.transform(Grayscale.RMY) - nImage.save("xisf-color-grayscale-rmy").second shouldBe "e75085eea073811aef41624aab318b48" - } - "color:grayscale Y" { - val mImage = Image.open(M82_COLOR_32_XISF.xisf()) - val nImage = mImage.transform(Grayscale.Y) - nImage.save("xisf-color-grayscale-y").second shouldBe "7a2bef966d460742533a1c8c3a74f1c5" - } - "!color:debayer" { - val mImage = Image.open(DEBAYER_FITS.xisf()) - val nImage = mImage.transform(AutoScreenTransformFunction) - nImage.save("xisf-color-debayer").second shouldBe "86b5bdd67dfd6bbf5495afae4bf2bc04" - } - "!color:no-debayer" { - val mImage = Image.open(DEBAYER_FITS.xisf(), false) - val nImage = mImage.transform(AutoScreenTransformFunction) - nImage.save("xisf-color-no-debayer").second shouldBe "958ccea020deec1f0c075042a9ba37c3" - } +class XisfTransformAlgorithmTest { + + @Test + fun monoRaw() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.save("xisf-mono-raw").second shouldBe "0dca7efedef5b3525f8037f401518b0b" + } + + @Test + fun monoVerticalFlip() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(VerticalFlip) + mImage.save("xisf-mono-vertical-flip").second shouldBe "d8250e5ac45d9a6ec04bed2a583a70b2" + } + + @Test + fun monoHorizontalFlip() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(HorizontalFlip) + mImage.save("xisf-mono-horizontal-flip").second shouldBe "9e2a05c331a5daeff20671a365e8b74c" + } + + @Test + fun monoVerticalAndHorizontalFlip() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(VerticalFlip, HorizontalFlip) + mImage.save("xisf-mono-vertical-horizontal-flip").second shouldBe "c7d55e870850c10619f389e3f9ec1243" + } + + @Test + fun monoSubframe() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) + nImage.width shouldBeExactly 16 + nImage.height shouldBeExactly 16 + nImage.mono.shouldBeTrue() + nImage.save("xisf-mono-subframe").second shouldBe "dc0ca19c5d40f90e4daac87fbab8f449" + } + + @Test + fun monoSharpen() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(Sharpen) + mImage.save("xisf-mono-sharpen").second shouldBe "08421f6afef422cdaa9f464a860808ce" + } + + @Test + fun monoMean() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(Mean) + mImage.save("xisf-mono-mean").second shouldBe "4127027d467ce9537049dc5f25f23ede" + } + + @Test + fun monoInvert() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(Invert) + mImage.save("xisf-mono-invert").second shouldBe "a6ec4a55225cb18f004e159a60137fe9" + } + + @Test + fun monoEmboss() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(Emboss) + mImage.save("xisf-mono-emboss").second shouldBe "2f07b3964b52f6b141b8cc45639b2287" + } + + @Test + fun monoEdges() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(Edges) + mImage.save("xisf-mono-edges").second shouldBe "897af72d536bca57cce77f09e396adb7" + } + + @Test + fun monoBlur() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(Blur) + mImage.save("xisf-mono-blur").second shouldBe "5a850ef20c0ebd461e676523823ff6ea" + } + + @Test + fun monoGaussianBlur() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) + mImage.save("xisf-mono-gaussian-blur").second shouldBe "cb7489cb1948e802ea4d92bba24e0739" + } + + @Test + fun monoStfMidTone01Shadow00Highlight10() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f)) + mImage.save("xisf-mono-stf-01-00-10").second shouldBe "91621dba5a58081347d156f2152bd8c6" + } + + @Test + fun monoStfMidTone09Shadow00Highlight10() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f)) + mImage.save("xisf-mono-stf-09-00-10").second shouldBe "9a67728cfc4f871ea8ab6557f381949e" + } + + @Test + fun monoStfMidTone01Shadow05Highlight10() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) + mImage.save("xisf-mono-stf-01-05-10").second shouldBe "c70b9caaaf88bba9c64891a2b3ba8033" + } + + @Test + fun monoStfMidTone09Shadow05Highlight10() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) + mImage.save("xisf-mono-stf-09-05-10").second shouldBe "4f4c49b96bd9aa417f22c59b8224ea90" + } + + @Test + fun monoStfMidTone01Shadow00Highlight05() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) + mImage.save("xisf-mono-stf-01-00-05").second shouldBe "b0c9598e3003d72a5fba617c30dc1821" + } + + @Test + fun monoStfMidTone09Shadow00Highlight05() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) + mImage.save("xisf-mono-stf-09-00-05").second shouldBe "5824ac8c2d44c2c8ff8121e0904bb77b" + } + + @Test + fun monoStfMidTone01Shadow04Highlight06() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) + mImage.save("xisf-mono-stf-01-04-06").second shouldBe "20417dfc43fc4c5cb425875614d8066c" + } + + @Test + fun monoStfMidTone09Shadow04Highlight06() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) + mImage.save("xisf-mono-stf-09-04-06").second shouldBe "13d13ad164a952a29f7ad76e1e51e7d0" + } + + @Test + fun monoAutoStf() { + val mImage = M82_MONO_8_XISF.xisf().asImage() + mImage.transform(AutoScreenTransformFunction) + mImage.save("xisf-mono-auto-stf").second shouldBe "9204a71df3770e8fe5ca49e3420eed72" + } + + @Test + fun colorRaw() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.save("xisf-color-raw").second shouldBe "764e326cc5260d81f3761112ad6a1969" + } + + @Test + fun colorVerticalFlip() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(VerticalFlip) + mImage.save("xisf-color-vertical-flip").second shouldBe "595d8d3e46e91d7adf7591e591a7a98d" + } + + @Test + fun colorHorizontalFlip() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(HorizontalFlip) + mImage.save("xisf-color-horizontal-flip").second shouldBe "b3d9609ef88db9b9215b4e21fdd4992a" + } + + @Test + fun colorVerticalAndHorizontalFlip() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(VerticalFlip, HorizontalFlip) + mImage.save("xisf-color-vertical-horizontal-flip").second shouldBe "1c9be1a7e5a66a989f6d0715c7ff30e4" + } + + @Test + fun colorSubframe() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + val nImage = mImage.transform(SubFrame(45, 70, 16, 16)) + nImage.width shouldBeExactly 16 + nImage.height shouldBeExactly 16 + nImage.mono.shouldBeFalse() + nImage.save("xisf-color-subframe").second shouldBe "dcf5bc8908ea6a32a5e98f14152f47b9" + } + + @Test + fun colorSharpen() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(Sharpen) + mImage.save("xisf-color-sharpen").second shouldBe "1017a88ff2caeb83a650d94d96c59347" + } + + @Test + fun colorMean() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(Mean) + mImage.save("xisf-color-mean").second shouldBe "eb948cab18442d008381f392bf43542a" + } + + @Test + fun colorInvert() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(Invert) + mImage.save("xisf-color-invert").second shouldBe "bbc97cdcde240873439bc29ff1adf169" + } + + @Test + fun colorEmboss() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(Emboss) + mImage.save("xisf-color-emboss").second shouldBe "fba6827f204df93f4aaf2f99b113a8f7" + } + + @Test + fun colorEdges() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(Edges) + mImage.save("xisf-color-edges").second shouldBe "71d6b5c936ab8165c7c1b63514d2da7b" + } + + @Test + fun colorBlur() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(Blur) + mImage.save("xisf-color-blur").second shouldBe "5c9f738a309fc86956d124f0f109eea2" + } + + @Test + fun colorGaussianBlur() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(GaussianBlur(sigma = 5.0, size = 9)) + mImage.save("xisf-color-gaussian-blur").second shouldBe "648beabcea0b486efc3f0c4ded632a06" + } + + @Test + fun colorStfMidTone01Shadow00Highlight10() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f)) + mImage.save("xisf-color-stf-01-00-10").second shouldBe "d84577785e44179f7af7c00807be5260" + } + + @Test + fun colorStfMidTone09Shadow00Highlight10() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f)) + mImage.save("xisf-color-stf-09-00-10").second shouldBe "11771bfe0e180e1efea4568902109013" + } + + @Test + fun colorStfMidTone01Shadow05Highlight10() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f, shadow = 0.5f)) + mImage.save("xisf-color-stf-01-05-10").second shouldBe "4e3b204d8a7ed06dfd05b7d010ef267b" + } + + @Test + fun colorStfMidTone09Shadow05Highlight10() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f, shadow = 0.5f)) + mImage.save("xisf-color-stf-09-05-10").second shouldBe "95c5b801978abdbb51afc9d0de12c218" + } + + @Test + fun colorStfMidTone01Shadow00Highlight05() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f, highlight = 0.5f)) + mImage.save("xisf-color-stf-01-00-05").second shouldBe "08322042c9e57102fec77a06b28c263d" + } + + @Test + fun colorStfMidTone09Shadow00Highlight05() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f, highlight = 0.5f)) + mImage.save("xisf-color-stf-09-00-05").second shouldBe "c99990d3a4bc6fc3aa7a8ac90d452419" + } + + @Test + fun colorStfMidTone01Shadow04Highlight06() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.1f, 0.4f, 0.6f)) + mImage.save("xisf-color-stf-01-04-06").second shouldBe "33bce2d7f7cde67d0ee618439f438ee7" + } + + @Test + fun colorStfMidTone09Shadow04Highlight06() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(ScreenTransformFunction(0.9f, 0.4f, 0.6f)) + mImage.save("xisf-color-stf-09-04-06").second shouldBe "923e56840bcea4462160e36f2e801f5e" + } + + @Test + fun colorAutoStf() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(AutoScreenTransformFunction) + mImage.save("xisf-color-auto-stf").second shouldBe "b1460451ad0f0580802d3d6d3a6750ba" + } + + @Test + fun colorScnrMaximumMask() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_MASK)) + mImage.save("xisf-color-scnr-maximum-mask").second shouldBe "e465a2fc0814055e089a3180c0235c8b" + } + + @Test + fun colorScnrAdditiveMask() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.ADDITIVE_MASK)) + mImage.save("xisf-color-scnr-additive-mask").second shouldBe "1016ec07253f869097d2d9c67194b3e3" + } + + @Test + fun colorScnrAverageNeutral() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.AVERAGE_NEUTRAL)) + mImage.save("xisf-color-scnr-average-neutral").second shouldBe "2f2583bfb2ae4a9e4441dc9b827196f7" + } + + @Test + fun colorScnrMaximumNeutral() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MAXIMUM_NEUTRAL)) + mImage.save("xisf-color-scnr-maximum-neutral").second shouldBe "dabd995db4fba052617b148b3700378d" + } + + @Test + fun colorScnrMinimumNeutral() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + mImage.transform(SubtractiveChromaticNoiseReduction(ImageChannel.RED, 1f, ProtectionMethod.MINIMUM_NEUTRAL)) + mImage.save("xisf-color-scnr-minimum-neutral").second shouldBe "b92e5130ec2a44c0afd1ca3782261e47" + } + + @Test + fun colorGrayscaleBt709() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + val nImage = mImage.transform(Grayscale.BT709) + nImage.save("xisf-color-grayscale-bt709").second shouldBe "bf78482866a0b3a216fa5262f28b0e5e" + } + + @Test + fun colorGrayscaleRmy() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + val nImage = mImage.transform(Grayscale.RMY) + nImage.save("xisf-color-grayscale-rmy").second shouldBe "e75085eea073811aef41624aab318b48" + } + + @Test + fun colorGrayscaleY() { + val mImage = M82_COLOR_32_XISF.xisf().asImage() + val nImage = mImage.transform(Grayscale.Y) + nImage.save("xisf-color-grayscale-y").second shouldBe "7a2bef966d460742533a1c8c3a74f1c5" + } + + @Test + @Disabled + fun colorDebayer() { + val mImage = DEBAYER_FITS.xisf().asImage() + val nImage = mImage.transform(AutoScreenTransformFunction) + nImage.save("xisf-color-debayer").second shouldBe "86b5bdd67dfd6bbf5495afae4bf2bc04" + } + + @Test + @Disabled + fun colorNoDebayer() { + val mImage = DEBAYER_FITS.xisf().asImage(false) + val nImage = mImage.transform(AutoScreenTransformFunction) + nImage.save("xisf-color-no-debayer").second shouldBe "958ccea020deec1f0c075042a9ba37c3" } } diff --git a/nebulosa-indi-protocol/src/test/kotlin/INDIXmlInputStreamTest.kt b/nebulosa-indi-protocol/src/test/kotlin/INDIXmlInputStreamTest.kt index 0b57e751f..c8f930824 100644 --- a/nebulosa-indi-protocol/src/test/kotlin/INDIXmlInputStreamTest.kt +++ b/nebulosa-indi-protocol/src/test/kotlin/INDIXmlInputStreamTest.kt @@ -1,294 +1,321 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.equality.shouldBeEqualToComparingFields import io.kotest.matchers.nulls.shouldNotBeNull import nebulosa.indi.protocol.* import nebulosa.indi.protocol.parser.INDIXmlInputStream +import org.junit.jupiter.api.Test import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.InputStream import java.io.PrintStream -class INDIXmlInputStreamTest : StringSpec() { - - init { - "def blob vector" { - DefBLOBVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.label = "LABEL" - it.group = "GROUP" - it.state = PropertyState.OK - it.perm = PropertyPermission.RW - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - - with(DefBLOB()) { - name = "NAME" - label = "LABEL" - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it +class INDIXmlInputStreamTest { + + @Test + fun defBlobVector() { + DefBLOBVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.label = "LABEL" + it.group = "GROUP" + it.state = PropertyState.OK + it.perm = PropertyPermission.RW + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + + with(DefBLOB()) { + name = "NAME" + label = "LABEL" + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "def switch vector" { - DefSwitchVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.label = "LABEL" - it.group = "GROUP" - it.state = PropertyState.OK - it.perm = PropertyPermission.RW - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - - with(DefSwitch()) { - name = "NAME" - label = "LABEL" - value = false - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun defSwitchVector() { + DefSwitchVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.label = "LABEL" + it.group = "GROUP" + it.state = PropertyState.OK + it.perm = PropertyPermission.RW + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + + with(DefSwitch()) { + name = "NAME" + label = "LABEL" + value = false + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "def number vector" { - DefNumberVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.label = "LABEL" - it.group = "GROUP" - it.state = PropertyState.OK - it.perm = PropertyPermission.RW - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - - with(DefNumber()) { - name = "NAME" - label = "LABEL" - value = 0.0 - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun defNumberVector() { + DefNumberVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.label = "LABEL" + it.group = "GROUP" + it.state = PropertyState.OK + it.perm = PropertyPermission.RW + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + + with(DefNumber()) { + name = "NAME" + label = "LABEL" + value = 0.0 + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "def light vector" { - DefLightVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.label = "LABEL" - it.group = "GROUP" - it.state = PropertyState.OK - it.timestamp = "2023-02-01T12:59:52" - - with(DefLight()) { - name = "NAME" - label = "LABEL" - value = PropertyState.OK - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun defLightVector() { + DefLightVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.label = "LABEL" + it.group = "GROUP" + it.state = PropertyState.OK + it.timestamp = "2023-02-01T12:59:52" + + with(DefLight()) { + name = "NAME" + label = "LABEL" + value = PropertyState.OK + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "def text vector" { - DefTextVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.label = "LABEL" - it.group = "GROUP" - it.state = PropertyState.OK - it.perm = PropertyPermission.RW - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - - with(DefText()) { - name = "NAME" - label = "LABEL" - value = "VALUE" - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun defTextVector() { + DefTextVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.label = "LABEL" + it.group = "GROUP" + it.state = PropertyState.OK + it.perm = PropertyPermission.RW + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + + with(DefText()) { + name = "NAME" + label = "LABEL" + value = "VALUE" + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "new blob vector" { - NewBLOBVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.timestamp = "2023-02-01T12:59:52" - - with(OneBLOB()) { - name = "NAME" - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun newBlobVector() { + NewBLOBVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.timestamp = "2023-02-01T12:59:52" + + with(OneBLOB()) { + name = "NAME" + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "new switch vector" { - NewSwitchVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.timestamp = "2023-02-01T12:59:52" - - with(OneSwitch()) { - name = "NAME" - value = false - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun newSwitchVector() { + NewSwitchVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.timestamp = "2023-02-01T12:59:52" + + with(OneSwitch()) { + name = "NAME" + value = false + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "new number vector" { - NewNumberVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.timestamp = "2023-02-01T12:59:52" - - with(OneNumber()) { - name = "NAME" - value = 0.0 - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun newNumberVector() { + NewNumberVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.timestamp = "2023-02-01T12:59:52" + + with(OneNumber()) { + name = "NAME" + value = 0.0 + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "new light vector" { - NewLightVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.timestamp = "2023-02-01T12:59:52" - - with(OneLight()) { - name = "NAME" - value = PropertyState.OK - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun newLightVector() { + NewLightVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.timestamp = "2023-02-01T12:59:52" + + with(OneLight()) { + name = "NAME" + value = PropertyState.OK + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "new text vector" { - NewTextVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.timestamp = "2023-02-01T12:59:52" - - with(OneText()) { - name = "NAME" - value = "VALUE" - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun newTextVector() { + NewTextVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.timestamp = "2023-02-01T12:59:52" + + with(OneText()) { + name = "NAME" + value = "VALUE" + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "set blob vector" { - SetBLOBVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.state = PropertyState.OK - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - it.message = "MESSAGE" - - with(OneBLOB()) { - name = "NAME" - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun setBlobVector() { + SetBLOBVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.state = PropertyState.OK + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + it.message = "MESSAGE" + + with(OneBLOB()) { + name = "NAME" + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "set switch vector" { - SetSwitchVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.state = PropertyState.OK - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - it.message = "MESSAGE" - - with(OneSwitch()) { - name = "NAME" - value = false - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun setSwitchVector() { + SetSwitchVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.state = PropertyState.OK + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + it.message = "MESSAGE" + + with(OneSwitch()) { + name = "NAME" + value = false + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "set number vector" { - SetNumberVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.state = PropertyState.OK - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - it.message = "MESSAGE" - - with(OneNumber()) { - name = "NAME" - value = 0.0 - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun setNumberVector() { + SetNumberVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.state = PropertyState.OK + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + it.message = "MESSAGE" + + with(OneNumber()) { + name = "NAME" + value = 0.0 + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "set light vector" { - SetLightVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.state = PropertyState.OK - it.timestamp = "2023-02-01T12:59:52" - it.message = "MESSAGE" - - with(OneLight()) { - name = "NAME" - value = PropertyState.OK - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun setLightVector() { + SetLightVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.state = PropertyState.OK + it.timestamp = "2023-02-01T12:59:52" + it.message = "MESSAGE" + + with(OneLight()) { + name = "NAME" + value = PropertyState.OK + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } - "set text vector" { - SetTextVector().also { - it.device = "DEVICE" - it.name = "NAME" - it.state = PropertyState.OK - it.timeout = 60.0 - it.timestamp = "2023-02-01T12:59:52" - it.message = "MESSAGE" - - with(OneText()) { - name = "NAME" - value = "VALUE" - it.elements.add(this) - } - - val parser = INDIXmlInputStream(it.toInputStream()) - parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it + } + + @Test + fun setTextVector() { + SetTextVector().also { + it.device = "DEVICE" + it.name = "NAME" + it.state = PropertyState.OK + it.timeout = 60.0 + it.timestamp = "2023-02-01T12:59:52" + it.message = "MESSAGE" + + with(OneText()) { + name = "NAME" + value = "VALUE" + it.elements.add(this) } + + val parser = INDIXmlInputStream(it.toInputStream()) + parser.readINDIProtocol().shouldNotBeNull() shouldBeEqualToComparingFields it } } diff --git a/nebulosa-io/src/test/kotlin/AbstractSeekableSinkAndSourceTest.kt b/nebulosa-io/src/test/kotlin/AbstractSeekableSinkAndSourceTest.kt index d7c219a92..4491cb013 100644 --- a/nebulosa-io/src/test/kotlin/AbstractSeekableSinkAndSourceTest.kt +++ b/nebulosa-io/src/test/kotlin/AbstractSeekableSinkAndSourceTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly @@ -6,288 +5,316 @@ import io.kotest.matchers.longs.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.io.SeekableSink import nebulosa.io.SeekableSource +import nebulosa.test.AbstractTest import okio.Buffer +import org.junit.jupiter.api.Test -abstract class AbstractSeekableSinkAndSourceTest : StringSpec() { +abstract class AbstractSeekableSinkAndSourceTest : AbstractTest() { abstract val sink: SeekableSink abstract val source: SeekableSource - init { - afterEach { - sink.seek(0) - source.seek(0) - } - - "byte" { - val buffer = Buffer() - buffer.writeByte(-12) - sink.seek(0) - sink.write(buffer, 1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 1 - - source.seek(0) - source.read(buffer, 1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 1 - buffer.readByte().toInt() shouldBeExactly -12 - } - "short-le" { - val buffer = Buffer() - buffer.writeShortLe(-23975) - sink.seek(0) - sink.write(buffer, 2) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 2 - - source.seek(0) - source.read(buffer, 2) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 2 - buffer.readShortLe().toInt() shouldBeExactly -23975 - } - "short" { - val buffer = Buffer() - buffer.writeShort(-23975) - sink.seek(0) - sink.write(buffer, 2) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 2 - - source.seek(0) - source.read(buffer, 2) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 2 - buffer.readShort().toInt() shouldBeExactly -23975 - } - "int-le" { - val buffer = Buffer() - buffer.writeIntLe(-145983) - sink.seek(0) - sink.write(buffer, 4) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 4 - - source.seek(0) - source.read(buffer, 4) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 4 - buffer.readIntLe() shouldBeExactly -145983 - } - "int" { - val buffer = Buffer() - buffer.writeInt(-145983) - sink.seek(0) - sink.write(buffer, 4) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 4 - - source.seek(0) - source.read(buffer, 4) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 4 - buffer.readInt() shouldBeExactly -145983 - } - "long-le" { - val buffer = Buffer() - buffer.writeLongLe(-3534545345345) - sink.seek(0) - sink.write(buffer, 8) - sink.exhausted.shouldBeTrue() - sink.position shouldBeExactly 8 - - source.seek(0) - source.read(buffer, 8) - source.exhausted.shouldBeTrue() - source.position shouldBeExactly 8 - buffer.readLongLe() shouldBeExactly -3534545345345 - } - "long" { - val buffer = Buffer() - buffer.writeLong(-3534545345345) - sink.seek(0) - sink.write(buffer, 8) - sink.exhausted.shouldBeTrue() - sink.position shouldBeExactly 8 - - source.seek(0) - source.read(buffer, 8) - source.exhausted.shouldBeTrue() - source.position shouldBeExactly 8 - buffer.readLong() shouldBeExactly -3534545345345 - } - "ascii" { - val buffer = Buffer() - buffer.writeString("Gê", Charsets.ISO_8859_1) - sink.seek(0) - sink.write(buffer, 2) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 2 - - source.seek(0) - source.read(buffer, 2) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 2 - buffer.readString(Charsets.ISO_8859_1) shouldBe "Gê" - } - "utf-8" { - val buffer = Buffer() - buffer.writeUtf8("\uD83D\uDE0A") - sink.seek(0) - sink.write(buffer, 4) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 4 - - source.seek(0) - source.read(buffer, 4) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 4 - buffer.readUtf8() shouldBe "\uD83D\uDE0A" - } - "seek and exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.seek(7) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - sink.write(buffer, 1) - sink.exhausted.shouldBeTrue() - sink.position shouldBeExactly 8 - - source.seek(7) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - source.read(buffer, 1) - source.exhausted.shouldBeTrue() - source.position shouldBeExactly 8 - buffer.readByte() shouldBe 99 - } - "seek and not exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.seek(6) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 6 - sink.write(buffer, 1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - - source.seek(6) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 6 - source.read(buffer, 1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - buffer.readByte() shouldBe 99 - } - "negative seek and exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.seek(-1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - sink.write(buffer, 1) - sink.exhausted.shouldBeTrue() - sink.position shouldBeExactly 8 - - source.seek(-1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - source.read(buffer, 1) - source.exhausted.shouldBeTrue() - source.position shouldBeExactly 8 - buffer.readByte() shouldBe 99 - } - "negative seek and not exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.seek(-2) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 6 - sink.write(buffer, 1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - - source.seek(-2) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 6 - source.read(buffer, 1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - buffer.readByte() shouldBe 99 - } - "skip and exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.skip(7) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - sink.write(buffer, 1) - sink.exhausted.shouldBeTrue() - sink.position shouldBeExactly 8 - - source.skip(7) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - source.read(buffer, 1) - source.exhausted.shouldBeTrue() - source.position shouldBeExactly 8 - buffer.readByte() shouldBe 99 - } - "skip and not exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.skip(6) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 6 - sink.write(buffer, 1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - - source.skip(6) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 6 - source.read(buffer, 1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - buffer.readByte() shouldBe 99 - } - "negative skip and exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.skip(-1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - sink.write(buffer, 1) - sink.exhausted.shouldBeTrue() - sink.position shouldBeExactly 8 - - source.skip(-1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - source.read(buffer, 1) - source.exhausted.shouldBeTrue() - source.position shouldBeExactly 8 - buffer.readByte() shouldBe 99 - } - "negative skip and not exhausted" { - val buffer = Buffer() - buffer.writeByte(99) - sink.skip(-2) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 6 - sink.write(buffer, 1) - sink.exhausted.shouldBeFalse() - sink.position shouldBeExactly 7 - - source.skip(-2) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 6 - source.read(buffer, 1) - source.exhausted.shouldBeFalse() - source.position shouldBeExactly 7 - buffer.readByte() shouldBe 99 - } + @Test + fun byte() { + val buffer = Buffer() + buffer.writeByte(-12) + sink.seek(0) + sink.write(buffer, 1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 1 + + source.seek(0) + source.read(buffer, 1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 1 + buffer.readByte().toInt() shouldBeExactly -12 + } + + @Test + fun shortLe() { + val buffer = Buffer() + buffer.writeShortLe(-23975) + sink.seek(0) + sink.write(buffer, 2) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 2 + + source.seek(0) + source.read(buffer, 2) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 2 + buffer.readShortLe().toInt() shouldBeExactly -23975 + } + + @Test + fun short() { + val buffer = Buffer() + buffer.writeShort(-23975) + sink.seek(0) + sink.write(buffer, 2) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 2 + + source.seek(0) + source.read(buffer, 2) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 2 + buffer.readShort().toInt() shouldBeExactly -23975 + } + + @Test + fun intLe() { + val buffer = Buffer() + buffer.writeIntLe(-145983) + sink.seek(0) + sink.write(buffer, 4) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 4 + + source.seek(0) + source.read(buffer, 4) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 4 + buffer.readIntLe() shouldBeExactly -145983 + } + + @Test + fun int() { + val buffer = Buffer() + buffer.writeInt(-145983) + sink.seek(0) + sink.write(buffer, 4) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 4 + + source.seek(0) + source.read(buffer, 4) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 4 + buffer.readInt() shouldBeExactly -145983 + } + + @Test + fun longLe() { + val buffer = Buffer() + buffer.writeLongLe(-3534545345345) + sink.seek(0) + sink.write(buffer, 8) + sink.exhausted.shouldBeTrue() + sink.position shouldBeExactly 8 + + source.seek(0) + source.read(buffer, 8) + source.exhausted.shouldBeTrue() + source.position shouldBeExactly 8 + buffer.readLongLe() shouldBeExactly -3534545345345 + } + + @Test + fun long() { + val buffer = Buffer() + buffer.writeLong(-3534545345345) + sink.seek(0) + sink.write(buffer, 8) + sink.exhausted.shouldBeTrue() + sink.position shouldBeExactly 8 + + source.seek(0) + source.read(buffer, 8) + source.exhausted.shouldBeTrue() + source.position shouldBeExactly 8 + buffer.readLong() shouldBeExactly -3534545345345 + } + + @Test + fun ascii() { + val buffer = Buffer() + buffer.writeString("Gê", Charsets.ISO_8859_1) + sink.seek(0) + sink.write(buffer, 2) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 2 + + source.seek(0) + source.read(buffer, 2) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 2 + buffer.readString(Charsets.ISO_8859_1) shouldBe "Gê" + } + + @Test + fun utf8() { + val buffer = Buffer() + buffer.writeUtf8("\uD83D\uDE0A") + sink.seek(0) + sink.write(buffer, 4) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 4 + + source.seek(0) + source.read(buffer, 4) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 4 + buffer.readUtf8() shouldBe "\uD83D\uDE0A" + } + + @Test + fun seekAndExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.seek(7) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + sink.write(buffer, 1) + sink.exhausted.shouldBeTrue() + sink.position shouldBeExactly 8 + + source.seek(7) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + source.read(buffer, 1) + source.exhausted.shouldBeTrue() + source.position shouldBeExactly 8 + buffer.readByte() shouldBe 99 + } + + @Test + fun seekAndNotExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.seek(6) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 6 + sink.write(buffer, 1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + + source.seek(6) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 6 + source.read(buffer, 1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + buffer.readByte() shouldBe 99 + } + + @Test + fun negativeSeekAndExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.seek(-1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + sink.write(buffer, 1) + sink.exhausted.shouldBeTrue() + sink.position shouldBeExactly 8 + + source.seek(-1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + source.read(buffer, 1) + source.exhausted.shouldBeTrue() + source.position shouldBeExactly 8 + buffer.readByte() shouldBe 99 + } + + @Test + fun negativeSeekAndNotExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.seek(-2) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 6 + sink.write(buffer, 1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + + source.seek(-2) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 6 + source.read(buffer, 1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + buffer.readByte() shouldBe 99 + } + + @Test + fun skipAndExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.skip(7) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + sink.write(buffer, 1) + sink.exhausted.shouldBeTrue() + sink.position shouldBeExactly 8 + + source.skip(7) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + source.read(buffer, 1) + source.exhausted.shouldBeTrue() + source.position shouldBeExactly 8 + buffer.readByte() shouldBe 99 + } + + @Test + fun skipAndNotExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.skip(6) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 6 + sink.write(buffer, 1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + + source.skip(6) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 6 + source.read(buffer, 1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + buffer.readByte() shouldBe 99 + } + + @Test + fun negativeSkipAndExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.skip(-1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + sink.write(buffer, 1) + sink.exhausted.shouldBeTrue() + sink.position shouldBeExactly 8 + + source.skip(-1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + source.read(buffer, 1) + source.exhausted.shouldBeTrue() + source.position shouldBeExactly 8 + buffer.readByte() shouldBe 99 + } + + @Test + fun negativeSkipAndNotExhausted() { + val buffer = Buffer() + buffer.writeByte(99) + sink.skip(-2) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 6 + sink.write(buffer, 1) + sink.exhausted.shouldBeFalse() + sink.position shouldBeExactly 7 + + source.skip(-2) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 6 + source.read(buffer, 1) + source.exhausted.shouldBeFalse() + source.position shouldBeExactly 7 + buffer.readByte() shouldBe 99 } } diff --git a/nebulosa-io/src/test/kotlin/Base64InputStreamTest.kt b/nebulosa-io/src/test/kotlin/Base64InputStreamTest.kt index 0b266fbda..86902ca3d 100644 --- a/nebulosa-io/src/test/kotlin/Base64InputStreamTest.kt +++ b/nebulosa-io/src/test/kotlin/Base64InputStreamTest.kt @@ -1,19 +1,18 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import nebulosa.io.Base64InputStream +import org.junit.jupiter.api.Test import java.util.* import kotlin.random.Random -class Base64InputStreamTest : StringSpec() { +class Base64InputStreamTest { - init { - "read" { - repeat(100) { - val bytes = Random.nextBytes(Random.nextInt(10, 100)) - val encoded = Base64.getEncoder().encodeToString(bytes) - val stream = Base64InputStream(encoded) - stream.readAllBytes() shouldBe bytes - } + @Test + fun read() { + repeat(100) { + val bytes = Random.nextBytes(Random.nextInt(10, 100)) + val encoded = Base64.getEncoder().encodeToString(bytes) + val stream = Base64InputStream(encoded) + stream.readAllBytes() shouldBe bytes } } } diff --git a/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetAndLengthTest.kt b/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetAndLengthTest.kt index 1fc9f0e40..aa068e7ab 100644 --- a/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetAndLengthTest.kt +++ b/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetAndLengthTest.kt @@ -1,6 +1,7 @@ import io.kotest.matchers.ints.shouldBeExactly import nebulosa.io.sink import nebulosa.io.source +import org.junit.jupiter.api.AfterEach class ByteArrayWithOffsetAndLengthTest : AbstractSeekableSinkAndSourceTest() { @@ -9,14 +10,13 @@ class ByteArrayWithOffsetAndLengthTest : AbstractSeekableSinkAndSourceTest() { override val sink = data.sink(5, 8) override val source = data.source(5, 8) - init { - afterEach { - for (i in 0..4) { - data[i].toInt() shouldBeExactly i - } - for (i in 13..15) { - data[i].toInt() shouldBeExactly i - } + @AfterEach + fun checkValuesAfterEach() { + for (i in 0..4) { + data[i].toInt() shouldBeExactly i + } + for (i in 13..15) { + data[i].toInt() shouldBeExactly i } } } diff --git a/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetTest.kt b/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetTest.kt index 62c2a0994..c60e28d67 100644 --- a/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetTest.kt +++ b/nebulosa-io/src/test/kotlin/ByteArrayWithOffsetTest.kt @@ -1,6 +1,7 @@ import io.kotest.matchers.ints.shouldBeExactly import nebulosa.io.sink import nebulosa.io.source +import org.junit.jupiter.api.AfterEach class ByteArrayWithOffsetTest : AbstractSeekableSinkAndSourceTest() { @@ -9,11 +10,10 @@ class ByteArrayWithOffsetTest : AbstractSeekableSinkAndSourceTest() { override val sink = data.sink(8) override val source = data.source(8) - init { - afterEach { - for (i in 0..7) { - data[i].toInt() shouldBeExactly i - } + @AfterEach + fun checkValuesAfterEach() { + for (i in 0..7) { + data[i].toInt() shouldBeExactly i } } } diff --git a/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetAndLengthTest.kt b/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetAndLengthTest.kt index 02bc3f0c9..9c8f4024b 100644 --- a/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetAndLengthTest.kt +++ b/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetAndLengthTest.kt @@ -1,6 +1,7 @@ import io.kotest.matchers.ints.shouldBeExactly import nebulosa.io.sink import nebulosa.io.source +import org.junit.jupiter.api.AfterEach import java.nio.ByteBuffer class ByteBufferWithOffsetAndLengthTest : AbstractSeekableSinkAndSourceTest() { @@ -11,14 +12,13 @@ class ByteBufferWithOffsetAndLengthTest : AbstractSeekableSinkAndSourceTest() { override val sink = data.sink(5, 8) override val source = data.source(5, 8) - init { - afterEach { - for (i in 0..4) { - bytes[i].toInt() shouldBeExactly i - } - for (i in 13..15) { - bytes[i].toInt() shouldBeExactly i - } + @AfterEach + fun checkValuesAfterEach() { + for (i in 0..4) { + bytes[i].toInt() shouldBeExactly i + } + for (i in 13..15) { + bytes[i].toInt() shouldBeExactly i } } } diff --git a/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetTest.kt b/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetTest.kt index f501d8691..785a5fba1 100644 --- a/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetTest.kt +++ b/nebulosa-io/src/test/kotlin/ByteBufferWithOffsetTest.kt @@ -1,6 +1,7 @@ import io.kotest.matchers.ints.shouldBeExactly import nebulosa.io.sink import nebulosa.io.source +import org.junit.jupiter.api.AfterEach import java.nio.ByteBuffer class ByteBufferWithOffsetTest : AbstractSeekableSinkAndSourceTest() { @@ -11,11 +12,10 @@ class ByteBufferWithOffsetTest : AbstractSeekableSinkAndSourceTest() { override val sink = data.sink(8) override val source = data.source(8) - init { - afterEach { - for (i in 0..7) { - bytes[i].toInt() shouldBeExactly i - } + @AfterEach + fun checkValuesAfterEach() { + for (i in 0..7) { + bytes[i].toInt() shouldBeExactly i } } } diff --git a/nebulosa-io/src/test/kotlin/ByteBufferWrappedWithOffsetAndLengthTest.kt b/nebulosa-io/src/test/kotlin/ByteBufferWrappedWithOffsetAndLengthTest.kt index a273d0b20..9802ee646 100644 --- a/nebulosa-io/src/test/kotlin/ByteBufferWrappedWithOffsetAndLengthTest.kt +++ b/nebulosa-io/src/test/kotlin/ByteBufferWrappedWithOffsetAndLengthTest.kt @@ -1,6 +1,7 @@ import io.kotest.matchers.ints.shouldBeExactly import nebulosa.io.sink import nebulosa.io.source +import org.junit.jupiter.api.AfterEach import java.nio.ByteBuffer class ByteBufferWrappedWithOffsetAndLengthTest : AbstractSeekableSinkAndSourceTest() { @@ -11,14 +12,13 @@ class ByteBufferWrappedWithOffsetAndLengthTest : AbstractSeekableSinkAndSourceTe override val sink = data.sink() override val source = data.source() - init { - afterEach { - for (i in 0..4) { - bytes[i].toInt() shouldBeExactly i - } - for (i in 13..15) { - bytes[i].toInt() shouldBeExactly i - } + @AfterEach + fun checkValuesAfterEach() { + for (i in 0..4) { + bytes[i].toInt() shouldBeExactly i + } + for (i in 13..15) { + bytes[i].toInt() shouldBeExactly i } } } diff --git a/nebulosa-io/src/test/kotlin/RandomAccessFileTest.kt b/nebulosa-io/src/test/kotlin/RandomAccessFileTest.kt index 3e759064b..a516a32f9 100644 --- a/nebulosa-io/src/test/kotlin/RandomAccessFileTest.kt +++ b/nebulosa-io/src/test/kotlin/RandomAccessFileTest.kt @@ -1,10 +1,10 @@ -import io.kotest.engine.spec.tempfile import nebulosa.io.seekableSink import nebulosa.io.seekableSource +import kotlin.io.path.writeBytes class RandomAccessFileTest : AbstractSeekableSinkAndSourceTest() { - private val file = tempfile() + private val file = tempPath("raf-", ".dat") override val sink = file.seekableSink() override val source = file.seekableSource() diff --git a/nebulosa-io/src/test/kotlin/RandomSourceTest.kt b/nebulosa-io/src/test/kotlin/RandomSourceTest.kt index 742d24245..0da1cb088 100644 --- a/nebulosa-io/src/test/kotlin/RandomSourceTest.kt +++ b/nebulosa-io/src/test/kotlin/RandomSourceTest.kt @@ -1,38 +1,45 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import nebulosa.io.source import okio.buffer +import org.junit.jupiter.api.Test import java.util.* -class RandomSourceTest : StringSpec() { +class RandomSourceTest { - init { - "read full segment" { - val source = Random(0).source(8192L).buffer() - val bytes = source.readByteArray(8192L) - bytes.isRandom().shouldBeTrue() - } - "read few bytes" { - val source = Random(0).source(8192L).buffer() - val bytes = source.readByteArray(256) - bytes.isRandom().shouldBeTrue() - } - "read many bytes" { - val source = Random(0).source(8192L * 4L).buffer() - val bytes = source.readByteArray(8192L * 4L) - bytes.isRandom().shouldBeTrue() - } + @Test + fun readFullSegmen() { + val source = Random(0).source(8192L).buffer() + val bytes = source.readByteArray(8192L) + bytes.isRandom().shouldBeTrue() } - private fun ByteArray.isRandom(): Boolean { - val counter = IntArray(256) + @Test + fun readFewByte() { + val source = Random(0).source(8192L).buffer() + val bytes = source.readByteArray(256) + bytes.isRandom().shouldBeTrue() + } - for (byte in this) { - counter[byte.toInt() and 0xFF]++ - } + @Test + fun readManyByte() { + val source = Random(0).source(8192L * 4L).buffer() + val bytes = source.readByteArray(8192L * 4L) + bytes.isRandom().shouldBeTrue() + } + + companion object { + + @JvmStatic + private fun ByteArray.isRandom(): Boolean { + val counter = IntArray(256) - val average = counter.average() + for (byte in this) { + counter[byte.toInt() and 0xFF]++ + } - return size / 256 == average.toInt() + val average = counter.average() + + return size / 256 == average.toInt() + } } } diff --git a/nebulosa-math/src/test/kotlin/AngleTest.kt b/nebulosa-math/src/test/kotlin/AngleTest.kt index e648f136d..4bdfe2bb1 100644 --- a/nebulosa-math/src/test/kotlin/AngleTest.kt +++ b/nebulosa-math/src/test/kotlin/AngleTest.kt @@ -1,180 +1,209 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.math.* +import org.junit.jupiter.api.Test import java.util.* -class AngleTest : StringSpec() { - - init { - "mas" { - 37000.mas shouldBe (0.000179381 plusOrMinus 1e-8) - 37000.mas.toArcsec shouldBe (37.0 plusOrMinus 0.1) - 37000.mas.toArcmin shouldBe (0.616666667 plusOrMinus 1e-8) - 37000.mas.toDegrees shouldBe (0.01027778 plusOrMinus 1e-8) - 37000.mas.toHours shouldBe (0.00068518 plusOrMinus 1e-8) - } - "arcsec" { - 37.arcsec shouldBe (0.000179381 plusOrMinus 1e-8) - 37.arcsec.toMas shouldBe (37000.0 plusOrMinus 0.1) - 37.arcsec.toArcmin shouldBe (0.616666667 plusOrMinus 1e-8) - 37.arcsec.toDegrees shouldBe (0.01027778 plusOrMinus 1e-8) - 37.arcsec.toHours shouldBe (0.00068518 plusOrMinus 1e-8) - } - "arcmin" { - 45.arcmin shouldBe (0.01308997 plusOrMinus 1e-8) - 45.arcmin.toMas shouldBe (2700000.0 plusOrMinus 0.1) - 45.arcmin.toArcsec shouldBe (2700.0 plusOrMinus 0.1) - 45.arcmin.toDegrees shouldBe (0.75 plusOrMinus 1e-8) - 45.arcmin.toHours shouldBe (0.05 plusOrMinus 1e-8) - } - "degrees" { - 6.deg shouldBe (0.10471975 plusOrMinus 1e-8) - 6.deg.toMas shouldBe (21600000.0 plusOrMinus 0.1) - 6.deg.toArcsec shouldBe (21600.0 plusOrMinus 0.1) - 6.deg.toArcmin shouldBe (360.0 plusOrMinus 0.1) - 6.deg.toHours shouldBe (0.4 plusOrMinus 1e-8) - } - "hours" { - 4.hours shouldBe (1.04719755 plusOrMinus 1e-8) - 4.hours.toMas shouldBe (216000000.0 plusOrMinus 0.1) - 4.hours.toArcsec shouldBe (216000.0 plusOrMinus 0.1) - 4.hours.toArcmin shouldBe (3600.0 plusOrMinus 0.1) - 4.hours.toDegrees shouldBe (60.0 plusOrMinus 0.1) - } - "dms" { - DMS(6, 45, 8.0) shouldBe (0.1178485 plusOrMinus 1e-8) - } - "plus" { - (0.5.rad + 0.5.rad) shouldBeExactly 1.0 - (0.5.rad + 0.5) shouldBeExactly 1.0 - } - "minus" { - (0.8.rad - 0.5.rad) shouldBe (0.3 plusOrMinus 1e-2) - (0.8.rad - 0.5) shouldBe (0.3 plusOrMinus 1e-2) - } - "times" { - (0.5.rad * 5) shouldBeExactly 2.5 - } - "div" { - (5.0.rad / 5) shouldBeExactly 1.0 - 5.0.rad / 5.0.rad shouldBeExactly 1.0 - } - "rem" { - (5.0.rad % 5) shouldBeExactly 0.0 - (5.0.rad % 5.0.rad) shouldBeExactly 0.0 - } - "parse decimal coordinates" { - "23.5634453".deg.toDegrees shouldBe 23.5634453 - "23.5634453".hours.toDegrees shouldBe 353.4516795 - } - "parse sexagesimal coordinates" { - "23 33 48.40308".deg.toDegrees shouldBe 23.5634453 - "23h 33 48.40308".hours.toDegrees shouldBe 353.4516795 - "23 33m 48.40308".deg.toDegrees shouldBe 23.5634453 - "23 33 48.40308s".deg.toDegrees shouldBe 23.5634453 - "23h 33m 48.40308".hours.toDegrees shouldBe 353.4516795 - "23 33m 48.40308s".deg.toDegrees shouldBe 23.5634453 - "23h 33m 48.40308s".hours.toDegrees shouldBe 353.4516795 - "-23° 33m 48.40308s".deg.toDegrees shouldBe -23.5634453 - " -23 33m 48.40308s ".deg.toDegrees shouldBe -23.5634453 - "-23 33.806718m".deg.toDegrees shouldBe -23.5634453 - "+23".deg.toDegrees shouldBe 23.0 - "-23".deg.toDegrees shouldBe -23.0 - "23h33m48.40308s".hours.toDegrees shouldBe 353.4516795 - "23h33m 48.40308\"".hours.toDegrees shouldBe 353.4516795 - "23h33'48.40308\"".hours.toDegrees shouldBe 353.4516795 - "-23°33'48.40308\"".hours.toDegrees shouldBe -353.4516795 - "-23°33'48.40308s 67.99".deg.toDegrees shouldBe -23.5634453 - "- 23°33'48.40308s 67.99".deg.toDegrees shouldBe -23.5634453 - "".deg.isFinite().shouldBeFalse() - "kkk".deg.isFinite().shouldBeFalse() - } - "format" { - val angle = "12h 30 1".hours - - angle.toHours shouldBe (12.5003 plusOrMinus 1e-4) - - AngleFormatter.Builder() - .degrees() - .secondsDecimalPlaces(2) - .separators("°", "'", "\"") - .locale(Locale.ENGLISH) - .build() - .format(angle) shouldBe "+187°30'15.00\"" - - AngleFormatter.Builder() - .hours() - .secondsDecimalPlaces(1) - .separators("h", "m", "s") - .locale(Locale.ENGLISH) - .build() - .format(angle) shouldBe "+12h30m01.0s" - - AngleFormatter.Builder() - .hours() - .noSign() - .secondsDecimalPlaces(0) - .locale(Locale.ENGLISH) - .build() - .format(angle) shouldBe "12:30:01" - - AngleFormatter.Builder() - .hours() - .noSign() - .noSeconds() - .build() - .format(angle) shouldBe "12:30" - - AngleFormatter.Builder() - .hours() - .noSign() - .noSeconds() - .separators("h", "m") - .build() - .format(angle) shouldBe "12h30m" - - val negativeAngle = "-43 00 45".deg - - AngleFormatter.Builder() - .degreesFormat("%02d") - .separators("°", "'", "\"") - .secondsDecimalPlaces(2) - .build() - .format(negativeAngle) shouldBe "-43°00'45.00\"" - - AngleFormatter.HMS - .format(angle) shouldBe "12h30m01.0s" - - AngleFormatter.SIGNED_DMS - .format(negativeAngle) shouldBe "-043°00'45.0\"" - - AngleFormatter.DMS - .format(angle) shouldBe "187°30'15.0\"" - - AngleFormatter.SIGNED_DMS - .newBuilder() - .secondsDecimalPlaces(2) - .degreesFormat("%03d") - .minutesFormat("%04d") - .secondsFormat("%02.05f") - .whitespaced() - .build() - .format(negativeAngle) shouldBe "-043 0000 45.00000" - - AngleFormatter.HMS.format(0.0) shouldBe "00h00m00.0s" - AngleFormatter.HMS.format(CIRCLE) shouldBe "00h00m00.0s" - } - "bug on round seconds" { - "23h59m60.0s".hours.formatHMS() shouldBe "00h00m00.0s" - - AngleFormatter.HMS - .format(Radians(6.283182643402501)) shouldBe "23h59m59.9s" - } - "bug on parse Unicode negative sign U+2212" { - "−29 00 28.1".deg.toDegrees shouldBe -29.007805555555557 - } +class AngleTest { + + @Test + fun mas() { + 37000.mas shouldBe (0.000179381 plusOrMinus 1e-8) + 37000.mas.toArcsec shouldBe (37.0 plusOrMinus 0.1) + 37000.mas.toArcmin shouldBe (0.616666667 plusOrMinus 1e-8) + 37000.mas.toDegrees shouldBe (0.01027778 plusOrMinus 1e-8) + 37000.mas.toHours shouldBe (0.00068518 plusOrMinus 1e-8) + } + + @Test + fun arcsec() { + 37.arcsec shouldBe (0.000179381 plusOrMinus 1e-8) + 37.arcsec.toMas shouldBe (37000.0 plusOrMinus 0.1) + 37.arcsec.toArcmin shouldBe (0.616666667 plusOrMinus 1e-8) + 37.arcsec.toDegrees shouldBe (0.01027778 plusOrMinus 1e-8) + 37.arcsec.toHours shouldBe (0.00068518 plusOrMinus 1e-8) + } + + @Test + fun arcmin() { + 45.arcmin shouldBe (0.01308997 plusOrMinus 1e-8) + 45.arcmin.toMas shouldBe (2700000.0 plusOrMinus 0.1) + 45.arcmin.toArcsec shouldBe (2700.0 plusOrMinus 0.1) + 45.arcmin.toDegrees shouldBe (0.75 plusOrMinus 1e-8) + 45.arcmin.toHours shouldBe (0.05 plusOrMinus 1e-8) + } + + @Test + fun degrees() { + 6.deg shouldBe (0.10471975 plusOrMinus 1e-8) + 6.deg.toMas shouldBe (21600000.0 plusOrMinus 0.1) + 6.deg.toArcsec shouldBe (21600.0 plusOrMinus 0.1) + 6.deg.toArcmin shouldBe (360.0 plusOrMinus 0.1) + 6.deg.toHours shouldBe (0.4 plusOrMinus 1e-8) + } + + @Test + fun hours() { + 4.hours shouldBe (1.04719755 plusOrMinus 1e-8) + 4.hours.toMas shouldBe (216000000.0 plusOrMinus 0.1) + 4.hours.toArcsec shouldBe (216000.0 plusOrMinus 0.1) + 4.hours.toArcmin shouldBe (3600.0 plusOrMinus 0.1) + 4.hours.toDegrees shouldBe (60.0 plusOrMinus 0.1) + } + + @Test + fun dms() { + DMS(6, 45, 8.0) shouldBe (0.1178485 plusOrMinus 1e-8) + } + + @Test + fun plus() { + (0.5.rad + 0.5.rad) shouldBeExactly 1.0 + (0.5.rad + 0.5) shouldBeExactly 1.0 + } + + @Test + fun minus() { + (0.8.rad - 0.5.rad) shouldBe (0.3 plusOrMinus 1e-2) + (0.8.rad - 0.5) shouldBe (0.3 plusOrMinus 1e-2) + } + + @Test + fun times() { + (0.5.rad * 5) shouldBeExactly 2.5 + } + + @Test + fun div() { + (5.0.rad / 5) shouldBeExactly 1.0 + 5.0.rad / 5.0.rad shouldBeExactly 1.0 + } + + @Test + fun rem() { + (5.0.rad % 5) shouldBeExactly 0.0 + (5.0.rad % 5.0.rad) shouldBeExactly 0.0 + } + + @Test + fun parseDecimalCoordinates() { + "23.5634453".deg.toDegrees shouldBe 23.5634453 + "23.5634453".hours.toDegrees shouldBe 353.4516795 + } + + @Test + fun parseSexagesimalCoordinates() { + "23 33 48.40308".deg.toDegrees shouldBe 23.5634453 + "23h 33 48.40308".hours.toDegrees shouldBe 353.4516795 + "23 33m 48.40308".deg.toDegrees shouldBe 23.5634453 + "23 33 48.40308s".deg.toDegrees shouldBe 23.5634453 + "23h 33m 48.40308".hours.toDegrees shouldBe 353.4516795 + "23 33m 48.40308s".deg.toDegrees shouldBe 23.5634453 + "23h 33m 48.40308s".hours.toDegrees shouldBe 353.4516795 + "-23° 33m 48.40308s".deg.toDegrees shouldBe -23.5634453 + " -23 33m 48.40308s ".deg.toDegrees shouldBe -23.5634453 + "-23 33.806718m".deg.toDegrees shouldBe -23.5634453 + "+23".deg.toDegrees shouldBe 23.0 + "-23".deg.toDegrees shouldBe -23.0 + "23h33m48.40308s".hours.toDegrees shouldBe 353.4516795 + "23h33m 48.40308\"".hours.toDegrees shouldBe 353.4516795 + "23h33'48.40308\"".hours.toDegrees shouldBe 353.4516795 + "-23°33'48.40308\"".hours.toDegrees shouldBe -353.4516795 + "-23°33'48.40308s 67.99".deg.toDegrees shouldBe -23.5634453 + "- 23°33'48.40308s 67.99".deg.toDegrees shouldBe -23.5634453 + "".deg.isFinite().shouldBeFalse() + "kkk".deg.isFinite().shouldBeFalse() + } + + @Test + fun format() { + val angle = "12h 30 1".hours + + angle.toHours shouldBe (12.5003 plusOrMinus 1e-4) + + AngleFormatter.Builder() + .degrees() + .secondsDecimalPlaces(2) + .separators("°", "'", "\"") + .locale(Locale.ENGLISH) + .build() + .format(angle) shouldBe "+187°30'15.00\"" + + AngleFormatter.Builder() + .hours() + .secondsDecimalPlaces(1) + .separators("h", "m", "s") + .locale(Locale.ENGLISH) + .build() + .format(angle) shouldBe "+12h30m01.0s" + + AngleFormatter.Builder() + .hours() + .noSign() + .secondsDecimalPlaces(0) + .locale(Locale.ENGLISH) + .build() + .format(angle) shouldBe "12:30:01" + + AngleFormatter.Builder() + .hours() + .noSign() + .noSeconds() + .build() + .format(angle) shouldBe "12:30" + + AngleFormatter.Builder() + .hours() + .noSign() + .noSeconds() + .separators("h", "m") + .build() + .format(angle) shouldBe "12h30m" + + val negativeAngle = "-43 00 45".deg + + AngleFormatter.Builder() + .degreesFormat("%02d") + .separators("°", "'", "\"") + .secondsDecimalPlaces(2) + .build() + .format(negativeAngle) shouldBe "-43°00'45.00\"" + + AngleFormatter.HMS + .format(angle) shouldBe "12h30m01.0s" + + AngleFormatter.SIGNED_DMS + .format(negativeAngle) shouldBe "-043°00'45.0\"" + + AngleFormatter.DMS + .format(angle) shouldBe "187°30'15.0\"" + + AngleFormatter.SIGNED_DMS + .newBuilder() + .secondsDecimalPlaces(2) + .degreesFormat("%03d") + .minutesFormat("%04d") + .secondsFormat("%02.05f") + .whitespaced() + .build() + .format(negativeAngle) shouldBe "-043 0000 45.00000" + + AngleFormatter.HMS.format(0.0) shouldBe "00h00m00.0s" + AngleFormatter.HMS.format(CIRCLE) shouldBe "00h00m00.0s" + } + + @Test + fun bugOnRoundSeconds() { + "23h59m60.0s".hours.formatHMS() shouldBe "00h00m00.0s" + + AngleFormatter.HMS + .format(Radians(6.283182643402501)) shouldBe "23h59m59.9s" + } + + @Test + fun bugOnParseUnicodeNegativeSignU2212() { + "−29 00 28.1".deg.toDegrees shouldBe -29.007805555555557 } } diff --git a/nebulosa-math/src/test/kotlin/DistanceTest.kt b/nebulosa-math/src/test/kotlin/DistanceTest.kt index 79e9a2185..088da182b 100644 --- a/nebulosa-math/src/test/kotlin/DistanceTest.kt +++ b/nebulosa-math/src/test/kotlin/DistanceTest.kt @@ -1,43 +1,51 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.math.* +import org.junit.jupiter.api.Test -class DistanceTest : StringSpec() { - - init { - - timeout = 1000L - - "au" { - 1.au.toKilometers shouldBe (149597870.700 plusOrMinus 1e-8) - 1.au.toMeters shouldBe (149597870700.0 plusOrMinus 1e-8) - 1.au.toLightYears shouldBe (0.000015813 plusOrMinus 1e-8) - } - "km" { - 8000.km shouldBe (0.00005348 plusOrMinus 1e-8) - 8000.km.toMeters shouldBe (8000000.0 plusOrMinus 1e-8) - 8000.km.toLightYears shouldBe (0.0000000008456 plusOrMinus 1e-12) - } - "plus" { - (0.5.au + 0.5.au) shouldBeExactly 1.0 - (0.5.au + 0.5) shouldBeExactly 1.0 - } - "minus" { - (0.8.au - 0.5.au) shouldBe (0.3 plusOrMinus 1e-2) - (0.8.au - 0.5) shouldBe (0.3 plusOrMinus 1e-2) - } - "times" { - (0.5.au * 5) shouldBeExactly 2.5 - } - "div" { - (5.0.au / 5) shouldBeExactly 1.0 - 5.0.au / 5.0.au shouldBeExactly 1.0 - } - "rem" { - (5.0.au % 5) shouldBeExactly 0.0 - (5.0.au % 5.0.au) shouldBeExactly 0.0 - } +class DistanceTest { + + @Test + fun au() { + 1.au.toKilometers shouldBe (149597870.700 plusOrMinus 1e-8) + 1.au.toMeters shouldBe (149597870700.0 plusOrMinus 1e-8) + 1.au.toLightYears shouldBe (0.000015813 plusOrMinus 1e-8) + } + + @Test + fun km() { + 8000.km shouldBe (0.00005348 plusOrMinus 1e-8) + 8000.km.toMeters shouldBe (8000000.0 plusOrMinus 1e-8) + 8000.km.toLightYears shouldBe (0.0000000008456 plusOrMinus 1e-12) + } + + @Test + fun plus() { + (0.5.au + 0.5.au) shouldBeExactly 1.0 + (0.5.au + 0.5) shouldBeExactly 1.0 + } + + @Test + fun minus() { + (0.8.au - 0.5.au) shouldBe (0.3 plusOrMinus 1e-2) + (0.8.au - 0.5) shouldBe (0.3 plusOrMinus 1e-2) + } + + @Test + fun times() { + (0.5.au * 5) shouldBeExactly 2.5 + } + + @Test + fun div() { + (5.0.au / 5) shouldBeExactly 1.0 + 5.0.au / 5.0.au shouldBeExactly 1.0 + } + + @Test + fun rem() { + (5.0.au % 5) shouldBeExactly 0.0 + (5.0.au % 5.0.au) shouldBeExactly 0.0 } } diff --git a/nebulosa-math/src/test/kotlin/Matrix3DTest.kt b/nebulosa-math/src/test/kotlin/Matrix3DTest.kt index 24cf7f82c..cb67aed89 100644 --- a/nebulosa-math/src/test/kotlin/Matrix3DTest.kt +++ b/nebulosa-math/src/test/kotlin/Matrix3DTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.doubles.plusOrMinus @@ -8,152 +7,172 @@ import nebulosa.math.Matrix3D import nebulosa.math.SEMICIRCLE import nebulosa.math.Vector3D import nebulosa.math.rad +import org.junit.jupiter.api.Test @Suppress("FloatingPointLiteralPrecision") -class Matrix3DTest : StringSpec() { - - init { - "rotate x" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val r = m.rotateX(0.3456789.rad) - r[0, 0] shouldBeExactly 2.0 - r[0, 1] shouldBeExactly 3.0 - r[0, 2] shouldBeExactly 2.0 - - r[1, 0] shouldBe (3.839043388235612460 plusOrMinus 1e-12) - r[1, 1] shouldBe (3.237033249594111899 plusOrMinus 1e-12) - r[1, 2] shouldBe (4.516714379005982719 plusOrMinus 1e-12) - - r[2, 0] shouldBe (1.806030415924501684 plusOrMinus 1e-12) - r[2, 1] shouldBe (3.085711545336372503 plusOrMinus 1e-12) - r[2, 2] shouldBe (3.687721683977873065 plusOrMinus 1e-12) - } - "rotate y" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val r = m.rotateY(0.3456789.rad) - r[0, 0] shouldBe (0.8651847818978159930 plusOrMinus 1e-12) - r[0, 1] shouldBe (1.467194920539316554 plusOrMinus 1e-12) - r[0, 2] shouldBe (0.1875137911274457342 plusOrMinus 1e-12) - - r[1, 0] shouldBeExactly 3.0 - r[1, 1] shouldBeExactly 2.0 - r[1, 2] shouldBeExactly 3.0 - - r[2, 0] shouldBe (3.500207892850427330 plusOrMinus 1e-12) - r[2, 1] shouldBe (4.779889022262298150 plusOrMinus 1e-12) - r[2, 2] shouldBe (5.381899160903798712 plusOrMinus 1e-12) - } - "rotate z" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val r = m.rotateZ(0.3456789.rad) - r[0, 0] shouldBe (2.898197754208926769 plusOrMinus 1e-12) - r[0, 1] shouldBe (3.500207892850427330 plusOrMinus 1e-12) - r[0, 2] shouldBe (2.898197754208926769 plusOrMinus 1e-12) - - r[1, 0] shouldBe (2.144865911309686813 plusOrMinus 1e-12) - r[1, 1] shouldBe (0.865184781897815993 plusOrMinus 1e-12) - r[1, 2] shouldBe (2.144865911309686813 plusOrMinus 1e-12) - - r[2, 0] shouldBeExactly 3.0 - r[2, 1] shouldBeExactly 4.0 - r[2, 2] shouldBeExactly 5.0 - } - "chain rotation" { - val m0 = Matrix3D.IDENTITY.rotateZ(SEMICIRCLE).rotateX(-SEMICIRCLE).rotateY(SEMICIRCLE) - val m1 = Matrix3D.rotZ(SEMICIRCLE).rotateX(-SEMICIRCLE).rotateY(SEMICIRCLE) - m0.matrix shouldBe m1.matrix - } - "plus matrix" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val n = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val r = m + n - - r[0, 0] shouldBeExactly 4.0 - r[0, 1] shouldBeExactly 6.0 - r[0, 2] shouldBeExactly 4.0 - - r[1, 0] shouldBeExactly 6.0 - r[1, 1] shouldBeExactly 4.0 - r[1, 2] shouldBeExactly 6.0 - - r[2, 0] shouldBeExactly 6.0 - r[2, 1] shouldBeExactly 8.0 - r[2, 2] shouldBeExactly 10.0 - } - "minus matrix" { - val m = Matrix3D(2.0, 3.0, 2.0, 6.0, 9.0, 6.0, 3.0, 6.0, 5.0) - val n = Matrix3D(1.0, 1.0, 5.0, 4.0, 5.0, 9.0, 2.0, 4.0, 4.0) - val r = m - n - - r[0, 0] shouldBeExactly 1.0 - r[0, 1] shouldBeExactly 2.0 - r[0, 2] shouldBeExactly -3.0 - - r[1, 0] shouldBeExactly 2.0 - r[1, 1] shouldBeExactly 4.0 - r[1, 2] shouldBeExactly -3.0 - - r[2, 0] shouldBeExactly 1.0 - r[2, 1] shouldBeExactly 2.0 - r[2, 2] shouldBeExactly 1.0 - } - "times scalar" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val r = m * 5.0 - - r[0, 0] shouldBeExactly 10.0 - r[0, 1] shouldBeExactly 15.0 - r[0, 2] shouldBeExactly 10.0 - - r[1, 0] shouldBeExactly 15.0 - r[1, 1] shouldBeExactly 10.0 - r[1, 2] shouldBeExactly 15.0 - - r[2, 0] shouldBeExactly 15.0 - r[2, 1] shouldBeExactly 20.0 - r[2, 2] shouldBeExactly 25.0 - } - "transpose" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val r = m.transposed - - r[0, 0] shouldBeExactly 2.0 - r[0, 1] shouldBeExactly 3.0 - r[0, 2] shouldBeExactly 3.0 - - r[1, 0] shouldBeExactly 3.0 - r[1, 1] shouldBeExactly 2.0 - r[1, 2] shouldBeExactly 4.0 - - r[2, 0] shouldBeExactly 2.0 - r[2, 1] shouldBeExactly 3.0 - r[2, 2] shouldBeExactly 5.0 - } - "times vector" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - val v = Vector3D(2.0, 3.0, 2.0) - val r = m * v - - r[0] shouldBeExactly 17.0 - r[1] shouldBeExactly 18.0 - r[2] shouldBeExactly 28.0 - } - "determinant" { - val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) - m.determinant shouldBeExactly -10.0 - } - "is empty" { - Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0).isEmpty().shouldBeFalse() - Matrix3D(2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0).isEmpty().shouldBeFalse() - Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeTrue() - } +class Matrix3DTest { + + @Test + fun rotateX() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val r = m.rotateX(0.3456789.rad) + r[0, 0] shouldBeExactly 2.0 + r[0, 1] shouldBeExactly 3.0 + r[0, 2] shouldBeExactly 2.0 + + r[1, 0] shouldBe (3.839043388235612460 plusOrMinus 1e-12) + r[1, 1] shouldBe (3.237033249594111899 plusOrMinus 1e-12) + r[1, 2] shouldBe (4.516714379005982719 plusOrMinus 1e-12) + + r[2, 0] shouldBe (1.806030415924501684 plusOrMinus 1e-12) + r[2, 1] shouldBe (3.085711545336372503 plusOrMinus 1e-12) + r[2, 2] shouldBe (3.687721683977873065 plusOrMinus 1e-12) + } + + @Test + fun rotateY() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val r = m.rotateY(0.3456789.rad) + r[0, 0] shouldBe (0.8651847818978159930 plusOrMinus 1e-12) + r[0, 1] shouldBe (1.467194920539316554 plusOrMinus 1e-12) + r[0, 2] shouldBe (0.1875137911274457342 plusOrMinus 1e-12) + + r[1, 0] shouldBeExactly 3.0 + r[1, 1] shouldBeExactly 2.0 + r[1, 2] shouldBeExactly 3.0 + + r[2, 0] shouldBe (3.500207892850427330 plusOrMinus 1e-12) + r[2, 1] shouldBe (4.779889022262298150 plusOrMinus 1e-12) + r[2, 2] shouldBe (5.381899160903798712 plusOrMinus 1e-12) + } + + @Test + fun rotateZ() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val r = m.rotateZ(0.3456789.rad) + r[0, 0] shouldBe (2.898197754208926769 plusOrMinus 1e-12) + r[0, 1] shouldBe (3.500207892850427330 plusOrMinus 1e-12) + r[0, 2] shouldBe (2.898197754208926769 plusOrMinus 1e-12) + + r[1, 0] shouldBe (2.144865911309686813 plusOrMinus 1e-12) + r[1, 1] shouldBe (0.865184781897815993 plusOrMinus 1e-12) + r[1, 2] shouldBe (2.144865911309686813 plusOrMinus 1e-12) + + r[2, 0] shouldBeExactly 3.0 + r[2, 1] shouldBeExactly 4.0 + r[2, 2] shouldBeExactly 5.0 + } + + @Test + fun chainRotation() { + val m0 = Matrix3D.IDENTITY.rotateZ(SEMICIRCLE).rotateX(-SEMICIRCLE).rotateY(SEMICIRCLE) + val m1 = Matrix3D.rotZ(SEMICIRCLE).rotateX(-SEMICIRCLE).rotateY(SEMICIRCLE) + m0.matrix shouldBe m1.matrix + } + + @Test + fun plusMatrix() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val n = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val r = m + n + + r[0, 0] shouldBeExactly 4.0 + r[0, 1] shouldBeExactly 6.0 + r[0, 2] shouldBeExactly 4.0 + + r[1, 0] shouldBeExactly 6.0 + r[1, 1] shouldBeExactly 4.0 + r[1, 2] shouldBeExactly 6.0 + + r[2, 0] shouldBeExactly 6.0 + r[2, 1] shouldBeExactly 8.0 + r[2, 2] shouldBeExactly 10.0 + } + + @Test + fun minusMatrix() { + val m = Matrix3D(2.0, 3.0, 2.0, 6.0, 9.0, 6.0, 3.0, 6.0, 5.0) + val n = Matrix3D(1.0, 1.0, 5.0, 4.0, 5.0, 9.0, 2.0, 4.0, 4.0) + val r = m - n + + r[0, 0] shouldBeExactly 1.0 + r[0, 1] shouldBeExactly 2.0 + r[0, 2] shouldBeExactly -3.0 + + r[1, 0] shouldBeExactly 2.0 + r[1, 1] shouldBeExactly 4.0 + r[1, 2] shouldBeExactly -3.0 + + r[2, 0] shouldBeExactly 1.0 + r[2, 1] shouldBeExactly 2.0 + r[2, 2] shouldBeExactly 1.0 + } + + @Test + fun timesScalar() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val r = m * 5.0 + + r[0, 0] shouldBeExactly 10.0 + r[0, 1] shouldBeExactly 15.0 + r[0, 2] shouldBeExactly 10.0 + + r[1, 0] shouldBeExactly 15.0 + r[1, 1] shouldBeExactly 10.0 + r[1, 2] shouldBeExactly 15.0 + + r[2, 0] shouldBeExactly 15.0 + r[2, 1] shouldBeExactly 20.0 + r[2, 2] shouldBeExactly 25.0 + } + + @Test + fun transpose() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val r = m.transposed + + r[0, 0] shouldBeExactly 2.0 + r[0, 1] shouldBeExactly 3.0 + r[0, 2] shouldBeExactly 3.0 + + r[1, 0] shouldBeExactly 3.0 + r[1, 1] shouldBeExactly 2.0 + r[1, 2] shouldBeExactly 4.0 + + r[2, 0] shouldBeExactly 2.0 + r[2, 1] shouldBeExactly 3.0 + r[2, 2] shouldBeExactly 5.0 + } + + @Test + fun timesVector() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + val v = Vector3D(2.0, 3.0, 2.0) + val r = m * v + + r[0] shouldBeExactly 17.0 + r[1] shouldBeExactly 18.0 + r[2] shouldBeExactly 28.0 + } + + @Test + fun determinant() { + val m = Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0) + m.determinant shouldBeExactly -10.0 + } + + @Test + fun isEmpty() { + Matrix3D(2.0, 3.0, 2.0, 3.0, 2.0, 3.0, 3.0, 4.0, 5.0).isEmpty().shouldBeFalse() + Matrix3D(2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 4.0, 0.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 5.0).isEmpty().shouldBeFalse() + Matrix3D(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0).isEmpty().shouldBeTrue() } } diff --git a/nebulosa-math/src/test/kotlin/Vector3DTest.kt b/nebulosa-math/src/test/kotlin/Vector3DTest.kt index 8a3cb9676..e1fc7357e 100644 --- a/nebulosa-math/src/test/kotlin/Vector3DTest.kt +++ b/nebulosa-math/src/test/kotlin/Vector3DTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.doubles.shouldBeExactly @@ -9,81 +8,113 @@ import nebulosa.math.Vector3D import nebulosa.math.deg import nebulosa.math.toDegrees import nebulosa.test.matchers.plusOrMinus +import org.junit.jupiter.api.Test -class Vector3DTest : StringSpec() { - - init { - "plus vector" { - Vector3D(2.0, 3.0, 2.0) + Vector3D(2.0, 3.0, 2.0) shouldBe Vector3D(4.0, 6.0, 4.0) - } - "minus vector" { - Vector3D(2.0, 3.0, 2.0) - Vector3D(1.0, 1.0, 5.0) shouldBe Vector3D(1.0, 2.0, -3.0) - } - "times scalar" { - Vector3D(2.0, 3.0, 2.0) * 5.0 shouldBe Vector3D(10.0, 15.0, 10.0) - } - "divide by scalar" { - Vector3D(2.0, 3.0, 2.0) / 2.0 shouldBe Vector3D(1.0, 1.5, 1.0) - } - "dot" { - val m = Vector3D(2.0, 3.0, 2.0) - val v = Vector3D(2.0, 3.0, 2.0) - m.dot(v) shouldBeExactly 17.0 - m.dot(-v) shouldBeExactly -17.0 - } - "cross" { - Vector3D(2.0, 3.0, 2.0).cross(Vector3D(3.0, 2.0, 3.0)) shouldBe Vector3D(5.0, 0.0, -5.0) - } - "is empty" { - Vector3D(2.0, 3.0, 2.0).isEmpty().shouldBeFalse() - Vector3D(2.0, 0.0, 0.0).isEmpty().shouldBeFalse() - Vector3D(0.0, 3.0, 0.0).isEmpty().shouldBeFalse() - Vector3D(0.0, 0.0, 4.0).isEmpty().shouldBeFalse() - Vector3D(0.0, 0.0, 0.0).isEmpty().shouldBeTrue() - } - "right angle" { - Vector3D.X.angle(Vector3D.Y) shouldBeExactly PIOVERTWO - } - "opposite" { - Vector3D(1.0, 2.0, 3.0).angle(Vector3D(-1.0, -2.0, -3.0)) shouldBeExactly PI - } - "collinear" { - Vector3D(2.0, -3.0, 1.0).angle(Vector3D(4.0, -6.0, 2.0)) shouldBeExactly 0.0 - } - "general" { - Vector3D(3.0, 4.0, 5.0).angle(Vector3D(1.0, 2.0, 2.0)).toDegrees shouldBeExactly 8.130102354156005 - } - "rotate around x axis" { - Vector3D.X.rotate(Vector3D.X, 90.0.deg) shouldBe Vector3D.X - } - "rotate around y axis" { - Vector3D.Y.rotate(Vector3D.Y, 90.0.deg) shouldBe Vector3D.Y - } - "rotate around z axis" { - Vector3D.Z.rotate(Vector3D.Z, 90.0.deg) shouldBe Vector3D.Z - } - "rotate" { - val v = Vector3D(1.0, 2.0, 3.0) - v.rotate(Vector3D.X, PI / 4.0) shouldBe (Vector3D(1.0, -0.707107, 3.535534) plusOrMinus 1e-6) - v.rotate(Vector3D.Y, PI / 4.0) shouldBe (Vector3D(2.828427, 2.0, 1.414213) plusOrMinus 1e-6) - v.rotate(Vector3D.Z, PI / 4.0) shouldBe (Vector3D(-0.707107, 2.12132, 3.0) plusOrMinus 1e-6) - val axis = Vector3D(3.0, 4.0, 5.0) - v.rotate(axis, 29.6512852.deg) shouldBe (Vector3D(1.21325856, 1.73061994, 3.08754891) plusOrMinus 1e-8) - v.rotate(axis, 120.3053274.deg) shouldBe (Vector3D(2.08677229, 1.63198489, 2.64234871) plusOrMinus 1e-8) - v.rotate(axis, 230.6512852.deg) shouldBe (Vector3D(1.69633894, 2.56816842, 2.12766190) plusOrMinus 1e-8) - v.rotate(axis, 359.6139797.deg) shouldBe (Vector3D(0.99810712, 2.00381299, 2.99808533) plusOrMinus 1e-8) - } - "no rotation" { - Vector3D(1.0, 2.0, 3.0).rotate(Vector3D.Y, 0.0) shouldBe Vector3D(1.0, 2.0, 3.0) - } - "plane" { - val a = Vector3D(1.0, -2.0, 1.0) - val b = Vector3D(4.0, -2.0, -2.0) - val c = Vector3D(4.0, 1.0, 4.0) - val d = Vector3D.plane(a, b, c) - d.x shouldBeExactly 9.0 - d.y shouldBeExactly -18.0 - d.z shouldBeExactly 9.0 - } +class Vector3DTest { + + @Test + fun plusVector() { + Vector3D(2.0, 3.0, 2.0) + Vector3D(2.0, 3.0, 2.0) shouldBe Vector3D(4.0, 6.0, 4.0) + } + + @Test + fun minusVector() { + Vector3D(2.0, 3.0, 2.0) - Vector3D(1.0, 1.0, 5.0) shouldBe Vector3D(1.0, 2.0, -3.0) + } + + @Test + fun timesScalar() { + Vector3D(2.0, 3.0, 2.0) * 5.0 shouldBe Vector3D(10.0, 15.0, 10.0) + } + + @Test + fun divideByScalar() { + Vector3D(2.0, 3.0, 2.0) / 2.0 shouldBe Vector3D(1.0, 1.5, 1.0) + } + + @Test + fun dot() { + val m = Vector3D(2.0, 3.0, 2.0) + val v = Vector3D(2.0, 3.0, 2.0) + m.dot(v) shouldBeExactly 17.0 + m.dot(-v) shouldBeExactly -17.0 + } + + @Test + fun cross() { + Vector3D(2.0, 3.0, 2.0).cross(Vector3D(3.0, 2.0, 3.0)) shouldBe Vector3D(5.0, 0.0, -5.0) + } + + @Test + fun isEmpty() { + Vector3D(2.0, 3.0, 2.0).isEmpty().shouldBeFalse() + Vector3D(2.0, 0.0, 0.0).isEmpty().shouldBeFalse() + Vector3D(0.0, 3.0, 0.0).isEmpty().shouldBeFalse() + Vector3D(0.0, 0.0, 4.0).isEmpty().shouldBeFalse() + Vector3D(0.0, 0.0, 0.0).isEmpty().shouldBeTrue() + } + + @Test + fun rightAngle() { + Vector3D.X.angle(Vector3D.Y) shouldBeExactly PIOVERTWO + } + + @Test + fun opposite() { + Vector3D(1.0, 2.0, 3.0).angle(Vector3D(-1.0, -2.0, -3.0)) shouldBeExactly PI + } + + @Test + fun collinear() { + Vector3D(2.0, -3.0, 1.0).angle(Vector3D(4.0, -6.0, 2.0)) shouldBeExactly 0.0 + } + + @Test + fun general() { + Vector3D(3.0, 4.0, 5.0).angle(Vector3D(1.0, 2.0, 2.0)).toDegrees shouldBeExactly 8.130102354156005 + } + + @Test + fun rotateAroundXAxis() { + Vector3D.X.rotate(Vector3D.X, 90.0.deg) shouldBe Vector3D.X + } + + @Test + fun rotateAroundYAxis() { + Vector3D.Y.rotate(Vector3D.Y, 90.0.deg) shouldBe Vector3D.Y + } + + @Test + fun rotateAroundZAxis() { + Vector3D.Z.rotate(Vector3D.Z, 90.0.deg) shouldBe Vector3D.Z + } + + @Test + fun rotate() { + val v = Vector3D(1.0, 2.0, 3.0) + v.rotate(Vector3D.X, PI / 4.0) shouldBe (Vector3D(1.0, -0.707107, 3.535534) plusOrMinus 1e-6) + v.rotate(Vector3D.Y, PI / 4.0) shouldBe (Vector3D(2.828427, 2.0, 1.414213) plusOrMinus 1e-6) + v.rotate(Vector3D.Z, PI / 4.0) shouldBe (Vector3D(-0.707107, 2.12132, 3.0) plusOrMinus 1e-6) + val axis = Vector3D(3.0, 4.0, 5.0) + v.rotate(axis, 29.6512852.deg) shouldBe (Vector3D(1.21325856, 1.73061994, 3.08754891) plusOrMinus 1e-8) + v.rotate(axis, 120.3053274.deg) shouldBe (Vector3D(2.08677229, 1.63198489, 2.64234871) plusOrMinus 1e-8) + v.rotate(axis, 230.6512852.deg) shouldBe (Vector3D(1.69633894, 2.56816842, 2.12766190) plusOrMinus 1e-8) + v.rotate(axis, 359.6139797.deg) shouldBe (Vector3D(0.99810712, 2.00381299, 2.99808533) plusOrMinus 1e-8) + } + + @Test + fun noRotation() { + Vector3D(1.0, 2.0, 3.0).rotate(Vector3D.Y, 0.0) shouldBe Vector3D(1.0, 2.0, 3.0) + } + + @Test + fun plane() { + val a = Vector3D(1.0, -2.0, 1.0) + val b = Vector3D(4.0, -2.0, -2.0) + val c = Vector3D(4.0, 1.0, 4.0) + val d = Vector3D.plane(a, b, c) + d.x shouldBeExactly 9.0 + d.y shouldBeExactly -18.0 + d.z shouldBeExactly 9.0 } } diff --git a/nebulosa-math/src/test/kotlin/VelocityTest.kt b/nebulosa-math/src/test/kotlin/VelocityTest.kt index 1d9ba7d72..028a4953e 100644 --- a/nebulosa-math/src/test/kotlin/VelocityTest.kt +++ b/nebulosa-math/src/test/kotlin/VelocityTest.kt @@ -1,45 +1,55 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.math.* +import org.junit.jupiter.api.Test -class VelocityTest : StringSpec() { - - init { - - timeout = 1000L - - "au/day" { - 1.auDay.toMetersPerSecond shouldBe (1731456.8368055555 plusOrMinus 1e-8) - 1.auDay.toKilometersPerSecond shouldBe (1731.4568368055554 plusOrMinus 1e-8) - } - "km/s" { - 8000.kms shouldBe (4.62038661891195 plusOrMinus 1e-8) - 8000.kms.toMetersPerSecond shouldBe (8000000.0 plusOrMinus 1e-8) - } - "m/s" { - 8000.ms shouldBe (0.00462038661891195 plusOrMinus 1e-8) - 8000.ms.toKilometersPerSecond shouldBe (8.0 plusOrMinus 1e-12) - } - "plus" { - (0.5.auDay + 0.5.auDay) shouldBeExactly 1.0 - (0.5.auDay + 0.5) shouldBeExactly 1.0 - } - "minus" { - (0.8.auDay - 0.5.auDay) shouldBe (0.3 plusOrMinus 1e-2) - (0.8.auDay - 0.5) shouldBe (0.3 plusOrMinus 1e-2) - } - "times" { - (0.5.auDay * 5) shouldBeExactly 2.5 - } - "div" { - (5.0.auDay / 5) shouldBeExactly 1.0 - 5.0.auDay / 5.0.auDay shouldBeExactly 1.0 - } - "rem" { - (5.0.auDay % 5) shouldBeExactly 0.0 - (5.0.auDay % 5.0.auDay) shouldBeExactly 0.0 - } +class VelocityTest { + + @Test + fun auDay() { + 1.auDay.toMetersPerSecond shouldBe (1731456.8368055555 plusOrMinus 1e-8) + 1.auDay.toKilometersPerSecond shouldBe (1731.4568368055554 plusOrMinus 1e-8) + } + + @Test + fun kms() { + 8000.kms shouldBe (4.62038661891195 plusOrMinus 1e-8) + 8000.kms.toMetersPerSecond shouldBe (8000000.0 plusOrMinus 1e-8) + } + + @Test + fun ms() { + 8000.ms shouldBe (0.00462038661891195 plusOrMinus 1e-8) + 8000.ms.toKilometersPerSecond shouldBe (8.0 plusOrMinus 1e-12) + } + + @Test + fun plus() { + (0.5.auDay + 0.5.auDay) shouldBeExactly 1.0 + (0.5.auDay + 0.5) shouldBeExactly 1.0 + } + + @Test + fun minus() { + (0.8.auDay - 0.5.auDay) shouldBe (0.3 plusOrMinus 1e-2) + (0.8.auDay - 0.5) shouldBe (0.3 plusOrMinus 1e-2) + } + + @Test + fun times() { + (0.5.auDay * 5) shouldBeExactly 2.5 + } + + @Test + fun div() { + (5.0.auDay / 5) shouldBeExactly 1.0 + 5.0.auDay / 5.0.auDay shouldBeExactly 1.0 + } + + @Test + fun rem() { + (5.0.auDay % 5) shouldBeExactly 0.0 + (5.0.auDay % 5.0.auDay) shouldBeExactly 0.0 } } diff --git a/nebulosa-nasa/src/test/kotlin/DafTest.kt b/nebulosa-nasa/src/test/kotlin/DafTest.kt index dbf37e9a0..84b0700e9 100644 --- a/nebulosa-nasa/src/test/kotlin/DafTest.kt +++ b/nebulosa-nasa/src/test/kotlin/DafTest.kt @@ -1,104 +1,107 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.ints.shouldBeGreaterThan import io.kotest.matchers.shouldBe import nebulosa.nasa.daf.RemoteDaf +import org.junit.jupiter.api.Test -class DafTest : StringSpec() { +class DafTest { - init { - "NAIF/DAF" { - val daf = RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de405.bsp") - daf.read() - val summaries = daf.summaries + @Test + fun naifDaf() { + val daf = RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de405.bsp") + daf.read() + val summaries = daf.summaries - summaries.size shouldBeExactly 15 + summaries.size shouldBeExactly 15 - val data = arrayOf( - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 1, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 2, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 3, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 4, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 5, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 6, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 7, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 8, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 9, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 10, 0, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 301, 3, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 399, 3, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 199, 1, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 299, 2, 1, 2), - arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 499, 4, 1, 2), - ) + val data = arrayOf( + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 1, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 2, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 3, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 4, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 5, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 6, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 7, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 8, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 9, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 10, 0, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 301, 3, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 399, 3, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 199, 1, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 299, 2, 1, 2), + arrayOf(-1.5778799588160586E9, 1.5778800641839132E9, 499, 4, 1, 2), + ) - summaries.forEachIndexed { index, summary -> - summary.name shouldBe "DE-405" + summaries.forEachIndexed { index, summary -> + summary.name shouldBe "DE-405" - summary.doubleAt(0) shouldBe data[index][0] - summary.doubleAt(1) shouldBe data[index][1] - summary.intAt(0) shouldBe data[index][2] - summary.intAt(1) shouldBe data[index][3] - summary.intAt(2) shouldBe data[index][4] - summary.intAt(3) shouldBe data[index][5] - summary.intAt(4) shouldBeGreaterThan 1024 - summary.intAt(5) shouldBeGreaterThan 1024 - } + summary.doubleAt(0) shouldBe data[index][0] + summary.doubleAt(1) shouldBe data[index][1] + summary.intAt(0) shouldBe data[index][2] + summary.intAt(1) shouldBe data[index][3] + summary.intAt(2) shouldBe data[index][4] + summary.intAt(3) shouldBe data[index][5] + summary.intAt(4) shouldBeGreaterThan 1024 + summary.intAt(5) shouldBeGreaterThan 1024 } - "DAF/SPK" { - val daf = RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de421.bsp") - daf.read() - val summaries = daf.summaries + } + + @Test + fun dafSpk() { + val daf = RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de421.bsp") + daf.read() + val summaries = daf.summaries - summaries.size shouldBeExactly 15 + summaries.size shouldBeExactly 15 - val data = arrayOf( - arrayOf(-3.1691952E9, 1.6968528E9, 1, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 2, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 3, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 4, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 5, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 6, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 7, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 8, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 9, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 10, 0, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 301, 3, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 399, 3, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 199, 1, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 299, 2, 1, 2), - arrayOf(-3.1691952E9, 1.6968528E9, 499, 4, 1, 2), - ) + val data = arrayOf( + arrayOf(-3.1691952E9, 1.6968528E9, 1, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 2, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 3, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 4, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 5, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 6, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 7, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 8, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 9, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 10, 0, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 301, 3, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 399, 3, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 199, 1, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 299, 2, 1, 2), + arrayOf(-3.1691952E9, 1.6968528E9, 499, 4, 1, 2), + ) - summaries.forEachIndexed { index, summary -> - summary.name shouldBe "DE-0421LE-0421" + summaries.forEachIndexed { index, summary -> + summary.name shouldBe "DE-0421LE-0421" - summary.doubleAt(0) shouldBe data[index][0] - summary.doubleAt(1) shouldBe data[index][1] - summary.intAt(0) shouldBe data[index][2] - summary.intAt(1) shouldBe data[index][3] - summary.intAt(2) shouldBe data[index][4] - summary.intAt(3) shouldBe data[index][5] - summary.intAt(4) shouldBeGreaterThan 512 - summary.intAt(5) shouldBeGreaterThan 512 - } + summary.doubleAt(0) shouldBe data[index][0] + summary.doubleAt(1) shouldBe data[index][1] + summary.intAt(0) shouldBe data[index][2] + summary.intAt(1) shouldBe data[index][3] + summary.intAt(2) shouldBe data[index][4] + summary.intAt(3) shouldBe data[index][5] + summary.intAt(4) shouldBeGreaterThan 512 + summary.intAt(5) shouldBeGreaterThan 512 } - "DAF/PCK" { - val daf = RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/moon_pa_de421_1900-2050.bpc") - daf.read() - val summaries = daf.summaries + } - summaries.size shouldBeExactly 1 - summaries[0].name shouldBe "de421.nio" - val data = arrayOf(-3.1557168E9, 1.609416E9, 31006, 1, 2, 641, 221284) + @Test + fun dafPck() { + val daf = RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/pck/moon_pa_de421_1900-2050.bpc") + daf.read() + val summaries = daf.summaries - summaries[0].doubleAt(0) shouldBe data[0] - summaries[0].doubleAt(1) shouldBe data[1] - summaries[0].intAt(0) shouldBe data[2] - summaries[0].intAt(1) shouldBe data[3] - summaries[0].intAt(2) shouldBe data[4] - summaries[0].intAt(3) shouldBe data[5] - summaries[0].intAt(4) shouldBe data[6] - } + summaries.size shouldBeExactly 1 + summaries[0].name shouldBe "de421.nio" + val data = arrayOf(-3.1557168E9, 1.609416E9, 31006, 1, 2, 641, 221284) + + summaries[0].doubleAt(0) shouldBe data[0] + summaries[0].doubleAt(1) shouldBe data[1] + summaries[0].intAt(0) shouldBe data[2] + summaries[0].intAt(1) shouldBe data[3] + summaries[0].intAt(2) shouldBe data[4] + summaries[0].intAt(3) shouldBe data[5] + summaries[0].intAt(4) shouldBe data[6] } } diff --git a/nebulosa-nasa/src/test/kotlin/RemoteDafTest.kt b/nebulosa-nasa/src/test/kotlin/RemoteDafTest.kt index 6eaa1a81a..268f1d620 100644 --- a/nebulosa-nasa/src/test/kotlin/RemoteDafTest.kt +++ b/nebulosa-nasa/src/test/kotlin/RemoteDafTest.kt @@ -1,26 +1,24 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly import nebulosa.nasa.daf.RemoteDaf -import java.nio.file.Files +import nebulosa.test.AbstractTest +import org.junit.jupiter.api.Test import java.nio.file.Paths import kotlin.io.path.deleteIfExists import kotlin.io.path.exists -class RemoteDafTest : StringSpec() { - - init { - val cachePath = Files.createTempDirectory("nebulosa") +class RemoteDafTest : AbstractTest() { + @Test + fun cacheFileShouldBeCreated() { + val cachePath = tempDirectory("daf-") val daf = RemoteDaf("https://ssd.jpl.nasa.gov/ftp/eph/planets/bsp/de441.bsp", cachePath) - "cache file should be created" { - val cacheFile = Paths.get("$cachePath", "70fc746e6e9cffbcaaa5af7e5d2b169a-0-1023.cache") - cacheFile.deleteIfExists() - daf.read() - daf.summaries.size shouldBeExactly 28 - cacheFile.exists().shouldBeTrue() - cacheFile.deleteIfExists() - } + val cacheFile = Paths.get("$cachePath", "70fc746e6e9cffbcaaa5af7e5d2b169a-0-1023.cache") + cacheFile.deleteIfExists() + daf.read() + daf.summaries.size shouldBeExactly 28 + cacheFile.exists().shouldBeTrue() + cacheFile.deleteIfExists() } } diff --git a/nebulosa-nasa/src/test/kotlin/SpkTest.kt b/nebulosa-nasa/src/test/kotlin/SpkTest.kt index 8f74a9b85..97c1c6789 100644 --- a/nebulosa-nasa/src/test/kotlin/SpkTest.kt +++ b/nebulosa-nasa/src/test/kotlin/SpkTest.kt @@ -1,48 +1,53 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.io.seekableSource import nebulosa.nasa.daf.RemoteDaf import nebulosa.nasa.daf.SourceDaf import nebulosa.nasa.spk.Spk +import nebulosa.test.AbstractTest +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.TimeYMDHMS import nebulosa.time.UTC -import java.nio.file.Path +import org.junit.jupiter.api.Test -class SpkTest : StringSpec() { +class SpkTest : AbstractTest() { - init { - "DE421: SSB - Earth Barycenter" { - val spk = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de421.bsp")) - val (p, v) = spk[0, 3]!!.compute(UTC(TimeYMDHMS(2022, 12, 8, 20, 7, 15.0))) + @Test + fun de421SsbEarthBarycenter() { + val spk = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de421.bsp")) + val (p, v) = spk[0, 3]!!.compute(UTC(TimeYMDHMS(2022, 12, 8, 20, 7, 15.0))) - p[0] shouldBe (2.226291206593103E-01 plusOrMinus 1e-6) - p[1] shouldBe (8.786267892743717E-01 plusOrMinus 1e-6) - p[2] shouldBe (3.811036725894850E-01 plusOrMinus 1e-6) - v[0] shouldBe (-1.700037773927917E-02 plusOrMinus 1e-6) - v[1] shouldBe (3.644822472013536E-03 plusOrMinus 1e-6) - v[2] shouldBe (1.580159029289274E-03 plusOrMinus 1e-6) - } - "DE405: SSB - Earth Barycenter" { - val spk = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de405.bsp")) - val (p, v) = spk[0, 3]!!.compute(UTC(TimeYMDHMS(2022, 12, 8, 20, 7, 15.0))) + p[0] shouldBe (2.226291206593103E-01 plusOrMinus 1e-6) + p[1] shouldBe (8.786267892743717E-01 plusOrMinus 1e-6) + p[2] shouldBe (3.811036725894850E-01 plusOrMinus 1e-6) + v[0] shouldBe (-1.700037773927917E-02 plusOrMinus 1e-6) + v[1] shouldBe (3.644822472013536E-03 plusOrMinus 1e-6) + v[2] shouldBe (1.580159029289274E-03 plusOrMinus 1e-6) + } + + @Test + fun de405SsbEarthBarycenter() { + val spk = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/planets/a_old_versions/de405.bsp")) + val (p, v) = spk[0, 3]!!.compute(UTC(TimeYMDHMS(2022, 12, 8, 20, 7, 15.0))) + + p[0] shouldBe (2.226291206593103E-01 plusOrMinus 1e-6) + p[1] shouldBe (8.786267892743717E-01 plusOrMinus 1e-6) + p[2] shouldBe (3.811036725894850E-01 plusOrMinus 1e-6) + v[0] shouldBe (-1.700037773927917E-02 plusOrMinus 1e-6) + v[1] shouldBe (3.644822472013536E-03 plusOrMinus 1e-6) + v[2] shouldBe (1.580159029289274E-03 plusOrMinus 1e-6) + } - p[0] shouldBe (2.226291206593103E-01 plusOrMinus 1e-6) - p[1] shouldBe (8.786267892743717E-01 plusOrMinus 1e-6) - p[2] shouldBe (3.811036725894850E-01 plusOrMinus 1e-6) - v[0] shouldBe (-1.700037773927917E-02 plusOrMinus 1e-6) - v[1] shouldBe (3.644822472013536E-03 plusOrMinus 1e-6) - v[2] shouldBe (1.580159029289274E-03 plusOrMinus 1e-6) - } - "65803 Didymos (Type 21)" { - val spk = Spk(SourceDaf(Path.of("../data/65803 Didymos.bsp").seekableSource())) - val (p, v) = spk[10, 2065803]!!.compute(UTC(TimeYMDHMS(2022, 12, 8, 20, 7, 15.0))) - p[0] shouldBe (1.231026319338612E-01 plusOrMinus 1e-2) - p[1] shouldBe (1.022833989843715E+00 plusOrMinus 1e-2) - p[2] shouldBe (4.567595812943146E-01 plusOrMinus 1e-2) - v[0] shouldBe (-1.739740083644565E-02 plusOrMinus 1e-2) - v[1] shouldBe (5.410812824810350E-03 plusOrMinus 1e-2) - v[2] shouldBe (3.549254153190032E-03 plusOrMinus 1e-2) - } + @Test + fun type2165803Didymos() { + val spk = Spk(SourceDaf(dataDirectory.concat("65803 Didymos.bsp").seekableSource().autoClose())) + val (p, v) = spk[10, 2065803]!!.compute(UTC(TimeYMDHMS(2022, 12, 8, 20, 7, 15.0))) + p[0] shouldBe (1.231026319338612E-01 plusOrMinus 1e-2) + p[1] shouldBe (1.022833989843715E+00 plusOrMinus 1e-2) + p[2] shouldBe (4.567595812943146E-01 plusOrMinus 1e-2) + v[0] shouldBe (-1.739740083644565E-02 plusOrMinus 1e-2) + v[1] shouldBe (5.410812824810350E-03 plusOrMinus 1e-2) + v[2] shouldBe (3.549254153190032E-03 plusOrMinus 1e-2) } } diff --git a/nebulosa-nova/src/test/kotlin/AstrometryTest.kt b/nebulosa-nova/src/test/kotlin/AstrometryTest.kt index 77571fc4c..3a5ab4509 100644 --- a/nebulosa-nova/src/test/kotlin/AstrometryTest.kt +++ b/nebulosa-nova/src/test/kotlin/AstrometryTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.io.seekableSource @@ -11,98 +10,118 @@ import nebulosa.nasa.daf.SourceDaf import nebulosa.nasa.spk.Spk import nebulosa.nova.astrometry.* import nebulosa.nova.position.Barycentric +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.TDB import nebulosa.time.TimeYMDHMS import nebulosa.time.UTC -import java.nio.file.Path +import org.junit.jupiter.api.Test -class AstrometryTest : StringSpec() { +class AstrometryTest { - init { - val de441 = Spk(RemoteDaf("https://ssd.jpl.nasa.gov/ftp/eph/planets/bsp/de441.bsp")) - val mar097 = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/satellites/mar097.bsp")) - val ura111 = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/satellites/ura111.bsp")) - val ceresSpk = Spk(SourceDaf(Path.of("../data/1 Ceres.bsp").seekableSource())) - val kernel = SpiceKernel(de441, mar097, ura111, ceresSpk) - val sun = kernel[10] - val moon = kernel[301] - val earth = kernel[399] - val mars = kernel[499] - val uranus = kernel[799] - val time = UTC(TimeYMDHMS(2022, 12, 25, 0, 0, 0.0)) + @Test + fun sunDE441() { + val astrometric = EARTH.at(TIME).observe(SUN) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (273.092531892 plusOrMinus 1e-9) + dec.toDegrees shouldBe (-23.406028751 plusOrMinus 1e-9) + } + + @Test + fun moonDE441() { + val astrometric = EARTH.at(TIME).observe(MOON) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (298.048320422 plusOrMinus 1e-9) + dec.toDegrees shouldBe (-25.911860684 plusOrMinus 1e-9) + } + + @Test + fun moonELPMPP02() { + val astrometric = EARTH.at(TIME).observe(EARTH + ELPMPP02) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (298.048320422 plusOrMinus 1e-4) + dec.toDegrees shouldBe (-25.911860684 plusOrMinus 1e-5) + } + + @Test + fun moonVSOP87EAndELPMPP02() { + val astrometric = VSOP87E.EARTH.at(TIME).observe(VSOP87E.EARTH + ELPMPP02) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (298.048320422 plusOrMinus 1e-4) + dec.toDegrees shouldBe (-25.911860684 plusOrMinus 1e-5) + } + + @Test + fun marsMAR097() { + val astrometric = EARTH.at(TIME).observe(MARS) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (68.127269738 plusOrMinus 1e-7) + dec.toDegrees shouldBe (24.681041544 plusOrMinus 1e-7) + } + + @Test + fun marsVSOP87E() { + val astrometric = VSOP87E.EARTH.at(TIME).observe(VSOP87E.MARS) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (68.127269738 plusOrMinus 1e-4) + dec.toDegrees shouldBe (24.681041544 plusOrMinus 1e-5) + } + + @Test + fun arielGUST86() { + val astrometric = EARTH.at(TIME).observe(URANUS + GUST86.ARIEL) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (42.621239755 plusOrMinus 1e-4) + dec.toDegrees shouldBe (15.978693112 plusOrMinus 1e-5) + } + + @Test + fun ceresAsteroid() { + val ceres = Asteroid( + semiMajorAxis = 2.769289292143484.au, + eccentricity = 0.07687465013145245, + inclination = 10.59127767086216.deg, + longitudeOfAscendingNode = 80.3011901917491.deg, // OM + argumentOfPerihelion = 73.80896808746482.deg, // W + meanAnomaly = 130.3159688200986.deg, + epoch = TDB(2458849.5), + ) + val astrometric = EARTH.at(TIME).observe(SUN + ceres) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (185.698485350 plusOrMinus 0.7) + dec.toDegrees shouldBe (9.929601380 plusOrMinus 0.3) + } + + @Test + fun ceresSPK() { + val ceres = KERNEL[2000001] + val astrometric = EARTH.at(TIME).observe(ceres) + val (ra, dec) = astrometric.equatorial() + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + ra.normalized.toDegrees shouldBe (185.698485350 plusOrMinus 1e-7) + dec.toDegrees shouldBe (9.929601380 plusOrMinus 1e-7) + } + + companion object { - "sun: DE441" { - val astrometric = earth.at(time).observe(sun) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (273.092531892 plusOrMinus 1e-9) - dec.toDegrees shouldBe (-23.406028751 plusOrMinus 1e-9) - } - "moon: DE441" { - val astrometric = earth.at(time).observe(moon) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (298.048320422 plusOrMinus 1e-9) - dec.toDegrees shouldBe (-25.911860684 plusOrMinus 1e-9) - } - "moon: ELPMPP02" { - val astrometric = earth.at(time).observe(earth + ELPMPP02) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (298.048320422 plusOrMinus 1e-4) - dec.toDegrees shouldBe (-25.911860684 plusOrMinus 1e-5) - } - "moon: VSOP87E + ELPMPP02" { - val astrometric = VSOP87E.EARTH.at(time).observe(VSOP87E.EARTH + ELPMPP02) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (298.048320422 plusOrMinus 1e-4) - dec.toDegrees shouldBe (-25.911860684 plusOrMinus 1e-5) - } - "mars: MAR097" { - val astrometric = earth.at(time).observe(mars) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (68.127269738 plusOrMinus 1e-7) - dec.toDegrees shouldBe (24.681041544 plusOrMinus 1e-7) - } - "mars: VSOP87E" { - val astrometric = VSOP87E.EARTH.at(time).observe(VSOP87E.MARS) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (68.127269738 plusOrMinus 1e-4) - dec.toDegrees shouldBe (24.681041544 plusOrMinus 1e-5) - } - "ariel: GUST86" { - val astrometric = earth.at(time).observe(uranus + GUST86.ARIEL) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (42.621239755 plusOrMinus 1e-4) - dec.toDegrees shouldBe (15.978693112 plusOrMinus 1e-5) - } - "ceres: Asteroid" { - val ceres = Asteroid( - semiMajorAxis = 2.769289292143484.au, - eccentricity = 0.07687465013145245, - inclination = 10.59127767086216.deg, - longitudeOfAscendingNode = 80.3011901917491.deg, // OM - argumentOfPerihelion = 73.80896808746482.deg, // W - meanAnomaly = 130.3159688200986.deg, - epoch = TDB(2458849.5), - ) - val astrometric = earth.at(time).observe(sun + ceres) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (185.698485350 plusOrMinus 0.7) - dec.toDegrees shouldBe (9.929601380 plusOrMinus 0.3) - } - "ceres: SPK" { - val ceres = kernel[2000001] - val astrometric = earth.at(time).observe(ceres) - val (ra, dec) = astrometric.equatorial() - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - ra.normalized.toDegrees shouldBe (185.698485350 plusOrMinus 1e-7) - dec.toDegrees shouldBe (9.929601380 plusOrMinus 1e-7) - } + @JvmStatic private val DE441 = Spk(RemoteDaf("https://ssd.jpl.nasa.gov/ftp/eph/planets/bsp/de441.bsp")) + @JvmStatic private val MAR097 = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/satellites/mar097.bsp")) + @JvmStatic private val URA111 = Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/satellites/ura111.bsp")) + @JvmStatic private val CERES = Spk(SourceDaf(dataDirectory.concat("1 Ceres.bsp").seekableSource())) + @JvmStatic private val KERNEL = SpiceKernel(DE441, MAR097, URA111, CERES) + @JvmStatic private val SUN = KERNEL[10] + @JvmStatic private val MOON = KERNEL[301] + @JvmStatic private val EARTH = KERNEL[399] + @JvmStatic private val MARS = KERNEL[499] + @JvmStatic private val URANUS = KERNEL[799] + @JvmStatic private val TIME = UTC(TimeYMDHMS(2022, 12, 25, 0, 0, 0.0)) } } diff --git a/nebulosa-nova/src/test/kotlin/ConstellationTest.kt b/nebulosa-nova/src/test/kotlin/ConstellationTest.kt index af0bda414..dfaf33529 100644 --- a/nebulosa-nova/src/test/kotlin/ConstellationTest.kt +++ b/nebulosa-nova/src/test/kotlin/ConstellationTest.kt @@ -1,22 +1,21 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import nebulosa.io.resource import nebulosa.math.deg import nebulosa.math.hours import nebulosa.nova.astrometry.Constellation import nebulosa.nova.position.ICRF +import org.junit.jupiter.api.Test -class ConstellationTest : StringSpec() { +class ConstellationTest { - init { - "find" { - for (line in resource("CONSTELLATION_TEST.txt")!!.bufferedReader().lines()) { - val parts = line.split(",") - val ra = parts[0].toDouble() - val dec = parts[1].toDouble() - val name = parts[2] - Constellation.find(ICRF.equatorial(ra.hours, dec.deg)).name shouldBe name - } + @Test + fun find() { + for (line in resource("CONSTELLATION_TEST.txt")!!.bufferedReader().lines()) { + val parts = line.split(",") + val ra = parts[0].toDouble() + val dec = parts[1].toDouble() + val name = parts[2] + Constellation.find(ICRF.equatorial(ra.hours, dec.deg)).name shouldBe name } } } diff --git a/nebulosa-nova/src/test/kotlin/ELPMPP02Test.kt b/nebulosa-nova/src/test/kotlin/ELPMPP02Test.kt index 40294822f..029478f7d 100644 --- a/nebulosa-nova/src/test/kotlin/ELPMPP02Test.kt +++ b/nebulosa-nova/src/test/kotlin/ELPMPP02Test.kt @@ -1,24 +1,25 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.nova.astrometry.ELPMPP02 import nebulosa.time.TDB import nebulosa.time.TimeJD +import org.junit.jupiter.api.Test -class ELPMPP02Test : StringSpec() { +class ELPMPP02Test { - init { - val moon = ELPMPP02 - val time = TDB(TimeJD(2459938.0, 0.5)) + @Test + fun moon() { + val (p, v) = ELPMPP02.compute(TIME) + p[0] shouldBe (1.013355885727306E-03 plusOrMinus 1e-9) + p[1] shouldBe (-1.903485709903833E-03 plusOrMinus 1e-9) + p[2] shouldBe (-1.047798412089101E-03 plusOrMinus 1e-9) + v[0] shouldBe (5.762732121285166E-04 plusOrMinus 1e-9) + v[1] shouldBe (2.476878261262097E-04 plusOrMinus 1e-9) + v[2] shouldBe (8.902329774047208E-05 plusOrMinus 1e-9) + } + + companion object { - "moon" { - val (p, v) = moon.compute(time) - p[0] shouldBe (1.013355885727306E-03 plusOrMinus 1e-9) - p[1] shouldBe (-1.903485709903833E-03 plusOrMinus 1e-9) - p[2] shouldBe (-1.047798412089101E-03 plusOrMinus 1e-9) - v[0] shouldBe (5.762732121285166E-04 plusOrMinus 1e-9) - v[1] shouldBe (2.476878261262097E-04 plusOrMinus 1e-9) - v[2] shouldBe (8.902329774047208E-05 plusOrMinus 1e-9) - } + @JvmStatic private val TIME = TDB(TimeJD(2459938.0, 0.5)) } } diff --git a/nebulosa-nova/src/test/kotlin/FixedStarTest.kt b/nebulosa-nova/src/test/kotlin/FixedStarTest.kt index 4796e4084..4d73846d4 100644 --- a/nebulosa-nova/src/test/kotlin/FixedStarTest.kt +++ b/nebulosa-nova/src/test/kotlin/FixedStarTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.shouldBe @@ -6,68 +5,80 @@ import nebulosa.math.* import nebulosa.nova.astrometry.FixedStar import nebulosa.nova.astrometry.VSOP87E import nebulosa.nova.position.Barycentric +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.IERS import nebulosa.time.IERSA import nebulosa.time.TimeYMDHMS import nebulosa.time.UTC -import java.nio.file.Path +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test import kotlin.io.path.inputStream import kotlin.math.truncate -class FixedStarTest : StringSpec() { +class FixedStarTest { - init { - val iersa = IERSA() - iersa.load(Path.of("../data/finals2000A.all").inputStream()) - IERS.attach(iersa) + @Test + fun polaris() { + // https://api.noctuasky.com/api/v1/skysources/name/NAME%20Polaris + val star = FixedStar( + 37.95456067.deg, 89.26410897.deg, + (44.48).mas, (-11.85).mas, (7.54).mas, (-16.42).kms, + ) - "polaris" { - // https://api.noctuasky.com/api/v1/skysources/name/NAME%20Polaris - val star = FixedStar( - 37.95456067.deg, 89.26410897.deg, - (44.48).mas, (-11.85).mas, (7.54).mas, (-16.42).kms, - ) + val astrometric = VSOP87E.EARTH.at(UTC(TimeYMDHMS(2024, 2, 17, 12, 0, 0.0))) + .observe(star) - val astrometric = VSOP87E.EARTH.at(UTC(TimeYMDHMS(2024, 2, 17, 12, 0, 0.0))) - .observe(star) + val (ra, dec) = astrometric.equatorialAtDate() - val (ra, dec) = astrometric.equatorialAtDate() + with(ra.normalized.hms()) { + truncate(this[0]) shouldBeExactly 3.0 + truncate(this[1]) shouldBeExactly 2.0 + this[2] shouldBe (3.9 plusOrMinus 20.0) + } + + with(dec.dms()) { + truncate(this[0]) shouldBeExactly 89.0 + truncate(this[1]) shouldBe (22.0 plusOrMinus 1.0) + this[2] shouldBe (15.8 plusOrMinus 50.0) + } + } - with(ra.normalized.hms()) { - truncate(this[0]) shouldBeExactly 3.0 - truncate(this[1]) shouldBeExactly 2.0 - this[2] shouldBe (3.9 plusOrMinus 20.0) - } + @Test + fun barnardsStar() { + // https://api.noctuasky.com/api/v1/skysources/name/NAME%20Barnard's%20Star + val star = FixedStar( + 269.452082497514.deg, 4.6933642650633.deg, + (-802.803).mas, (10362.542).mas, (547.451).mas, (-110.51).kms, + ) - with(dec.dms()) { - truncate(this[0]) shouldBeExactly 89.0 - truncate(this[1]) shouldBe (22.0 plusOrMinus 1.0) - this[2] shouldBe (15.8 plusOrMinus 50.0) - } + val astrometric = VSOP87E.EARTH.at(UTC(TimeYMDHMS(2024, 2, 17, 12, 0, 0.0))) + .observe(star) + + val (ra, dec) = astrometric.equatorialAtDate() + + with(ra.normalized.hms()) { + truncate(this[0]) shouldBeExactly 17.0 + truncate(this[1]) shouldBeExactly 58.0 + this[2] shouldBe (57.8 plusOrMinus 1.0) } - "barnard's star" { - // https://api.noctuasky.com/api/v1/skysources/name/NAME%20Barnard's%20Star - val star = FixedStar( - 269.452082497514.deg, 4.6933642650633.deg, - (-802.803).mas, (10362.542).mas, (547.451).mas, (-110.51).kms, - ) - val astrometric = VSOP87E.EARTH.at(UTC(TimeYMDHMS(2024, 2, 17, 12, 0, 0.0))) - .observe(star) + with(dec.dms()) { + truncate(this[0]) shouldBeExactly 4.0 + truncate(this[1]) shouldBeExactly 45.0 + this[2] shouldBe (25.5 plusOrMinus 10.0) + } + } - val (ra, dec) = astrometric.equatorialAtDate() + companion object { - with(ra.normalized.hms()) { - truncate(this[0]) shouldBeExactly 17.0 - truncate(this[1]) shouldBeExactly 58.0 - this[2] shouldBe (57.8 plusOrMinus 1.0) - } + @JvmStatic private val IERSA = IERSA() - with(dec.dms()) { - truncate(this[0]) shouldBeExactly 4.0 - truncate(this[1]) shouldBeExactly 45.0 - this[2] shouldBe (25.5 plusOrMinus 10.0) - } + @JvmStatic + @BeforeAll + fun loadIERS() { + dataDirectory.concat("finals2000A.all").inputStream().use(IERSA::load) + IERS.attach(IERSA) } } } diff --git a/nebulosa-nova/src/test/kotlin/GUST86Test.kt b/nebulosa-nova/src/test/kotlin/GUST86Test.kt index 458aee5c2..42cda34e6 100644 --- a/nebulosa-nova/src/test/kotlin/GUST86Test.kt +++ b/nebulosa-nova/src/test/kotlin/GUST86Test.kt @@ -1,67 +1,74 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.nova.astrometry.GUST86 import nebulosa.time.TDB import nebulosa.time.TimeJD +import org.junit.jupiter.api.Test -class GUST86Test : StringSpec() { +class GUST86Test { - init { - "ariel" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = GUST86.ARIEL.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (-4.451100762924695E-04 plusOrMinus 1e-5) - p[1] shouldBe (4.127293811714091E-04 plusOrMinus 1e-5) - p[2] shouldBe (-1.122529644334287E-03 plusOrMinus 1e-5) - v[0] shouldBe (-2.905800702342619E-03 plusOrMinus 1e-5) - v[1] shouldBe (3.018674943703828E-04 plusOrMinus 1e-5) - v[2] shouldBe (1.261178539831990E-03 plusOrMinus 1e-5) - } - "umbriel" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = GUST86.UMBRIEL.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (-1.611384907091204E-03 plusOrMinus 1e-5) - p[1] shouldBe (1.606295364496704E-04 plusOrMinus 1e-5) - p[2] shouldBe (7.209771384443374E-04 plusOrMinus 1e-5) - v[0] shouldBe (9.751794785461074E-04 plusOrMinus 1e-5) - v[1] shouldBe (-8.767828036553259E-04 plusOrMinus 1e-5) - v[2] shouldBe (2.364500180196773E-03 plusOrMinus 1e-5) - } - "titania" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = GUST86.TITANIA.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (-2.232644660544111E-03 plusOrMinus 1e-5) - p[1] shouldBe (9.515348359690534E-04 plusOrMinus 1e-5) - p[2] shouldBe (-1.618326368294999E-03 plusOrMinus 1e-5) - v[0] shouldBe (-1.281512705822040E-03 plusOrMinus 1e-5) - v[1] shouldBe (-1.711924613002539E-04 plusOrMinus 1e-5) - v[2] shouldBe (1.660414235776199E-03 plusOrMinus 1e-5) - } - "oberon" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = GUST86.OBERON.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (3.195547478166968E-03 plusOrMinus 1e-5) - p[1] shouldBe (-1.226712898705434E-03 plusOrMinus 1e-5) - p[2] shouldBe (1.866878239761929E-03 plusOrMinus 1e-5) - v[0] shouldBe (9.686636758969398E-04 plusOrMinus 1e-5) - v[1] shouldBe (2.027942751407152E-04 plusOrMinus 1e-5) - v[2] shouldBe (-1.527974319936715E-03 plusOrMinus 1e-5) - } - "miranda" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = GUST86.MIRANDA.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (-7.240222342711871E-04 plusOrMinus 1e-5) - p[1] shouldBe (7.574953585547300E-05 plusOrMinus 1e-5) - p[2] shouldBe (4.707549340071754E-04 plusOrMinus 1e-5) - v[0] shouldBe (1.941801978392934E-03 plusOrMinus 1e-5) - v[1] shouldBe (-1.068242211440700E-03 plusOrMinus 1e-5) - v[2] shouldBe (3.164065185998637E-03 plusOrMinus 1e-5) - } + @Test + fun ariel() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = GUST86.ARIEL.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (-4.451100762924695E-04 plusOrMinus 1e-5) + p[1] shouldBe (4.127293811714091E-04 plusOrMinus 1e-5) + p[2] shouldBe (-1.122529644334287E-03 plusOrMinus 1e-5) + v[0] shouldBe (-2.905800702342619E-03 plusOrMinus 1e-5) + v[1] shouldBe (3.018674943703828E-04 plusOrMinus 1e-5) + v[2] shouldBe (1.261178539831990E-03 plusOrMinus 1e-5) + } + + @Test + fun umbriel() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = GUST86.UMBRIEL.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (-1.611384907091204E-03 plusOrMinus 1e-5) + p[1] shouldBe (1.606295364496704E-04 plusOrMinus 1e-5) + p[2] shouldBe (7.209771384443374E-04 plusOrMinus 1e-5) + v[0] shouldBe (9.751794785461074E-04 plusOrMinus 1e-5) + v[1] shouldBe (-8.767828036553259E-04 plusOrMinus 1e-5) + v[2] shouldBe (2.364500180196773E-03 plusOrMinus 1e-5) + } + + @Test + fun titania() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = GUST86.TITANIA.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (-2.232644660544111E-03 plusOrMinus 1e-5) + p[1] shouldBe (9.515348359690534E-04 plusOrMinus 1e-5) + p[2] shouldBe (-1.618326368294999E-03 plusOrMinus 1e-5) + v[0] shouldBe (-1.281512705822040E-03 plusOrMinus 1e-5) + v[1] shouldBe (-1.711924613002539E-04 plusOrMinus 1e-5) + v[2] shouldBe (1.660414235776199E-03 plusOrMinus 1e-5) + } + + @Test + fun oberon() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = GUST86.OBERON.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (3.195547478166968E-03 plusOrMinus 1e-5) + p[1] shouldBe (-1.226712898705434E-03 plusOrMinus 1e-5) + p[2] shouldBe (1.866878239761929E-03 plusOrMinus 1e-5) + v[0] shouldBe (9.686636758969398E-04 plusOrMinus 1e-5) + v[1] shouldBe (2.027942751407152E-04 plusOrMinus 1e-5) + v[2] shouldBe (-1.527974319936715E-03 plusOrMinus 1e-5) + } + + @Test + fun miranda() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = GUST86.MIRANDA.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (-7.240222342711871E-04 plusOrMinus 1e-5) + p[1] shouldBe (7.574953585547300E-05 plusOrMinus 1e-5) + p[2] shouldBe (4.707549340071754E-04 plusOrMinus 1e-5) + v[0] shouldBe (1.941801978392934E-03 plusOrMinus 1e-5) + v[1] shouldBe (-1.068242211440700E-03 plusOrMinus 1e-5) + v[2] shouldBe (3.164065185998637E-03 plusOrMinus 1e-5) } } diff --git a/nebulosa-nova/src/test/kotlin/GeographicPositionTest.kt b/nebulosa-nova/src/test/kotlin/GeographicPositionTest.kt index 30a74a811..62d5ddb9b 100644 --- a/nebulosa-nova/src/test/kotlin/GeographicPositionTest.kt +++ b/nebulosa-nova/src/test/kotlin/GeographicPositionTest.kt @@ -1,38 +1,48 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.math.deg import nebulosa.math.formatHMS import nebulosa.math.m import nebulosa.nova.position.Geoid +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.IERS import nebulosa.time.IERSA import nebulosa.time.TimeYMDHMS import nebulosa.time.UTC -import java.nio.file.Path +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test import kotlin.io.path.inputStream -class GeographicPositionTest : StringSpec() { +class GeographicPositionTest { - init { - val iersa = IERSA() - iersa.load(Path.of("../data/finals2000A.all").inputStream()) - IERS.attach(iersa) + @Test + fun lst() { + val position = Geoid.IERS2010.lonLat((-45.4227).deg, 0.0) + position.lstAt(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0))).formatHMS() shouldBe "15h42m47.1s" + position.lstAt(UTC(TimeYMDHMS(2024, 1, 1, 12, 0, 0.0))).formatHMS() shouldBe "15h40m53.1s" + position.lstAt(UTC(TimeYMDHMS(2025, 1, 1, 12, 0, 0.0))).formatHMS() shouldBe "15h43m52.8s" + } - "lst" { - val position = Geoid.IERS2010.lonLat((-45.4227).deg, 0.0) - position.lstAt(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0))).formatHMS() shouldBe "15h42m47.1s" - position.lstAt(UTC(TimeYMDHMS(2024, 1, 1, 12, 0, 0.0))).formatHMS() shouldBe "15h40m53.1s" - position.lstAt(UTC(TimeYMDHMS(2025, 1, 1, 12, 0, 0.0))).formatHMS() shouldBe "15h43m52.8s" - } - "xyz" { - val latitude = "-23 32 51.00".deg - val longitude = "-46 38 10.00".deg - val position = Geoid.IERS2010.lonLat(longitude, latitude, 853.0.m) + @Test + fun xyz() { + val latitude = "-23 32 51.00".deg + val longitude = "-46 38 10.00".deg + val position = Geoid.IERS2010.lonLat(longitude, latitude, 853.0.m) + + position.x shouldBe (-2.8434040742871705E-5 plusOrMinus 1e-13) + position.y shouldBe (2.685480929038628E-5 plusOrMinus 1e-13) + position.z shouldBe (-1.693045603541487E-5 plusOrMinus 1e-13) + } + + companion object { - position.x shouldBe (-2.8434040742871705E-5 plusOrMinus 1e-13) - position.y shouldBe (2.685480929038628E-5 plusOrMinus 1e-13) - position.z shouldBe (-1.693045603541487E-5 plusOrMinus 1e-13) + @JvmStatic + @BeforeAll + fun loadIERS() { + val iersa = IERSA() + dataDirectory.concat("finals2000A.all").inputStream().use(iersa::load) + IERS.attach(iersa) } } } diff --git a/nebulosa-nova/src/test/kotlin/ICRFTest.kt b/nebulosa-nova/src/test/kotlin/ICRFTest.kt index 8725481cc..955b5ece0 100644 --- a/nebulosa-nova/src/test/kotlin/ICRFTest.kt +++ b/nebulosa-nova/src/test/kotlin/ICRFTest.kt @@ -1,53 +1,65 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.math.* import nebulosa.nova.position.Geoid import nebulosa.nova.position.ICRF +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.IERS import nebulosa.time.IERSA import nebulosa.time.TT import nebulosa.time.TimeYMDHMS -import java.nio.file.Path +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test import kotlin.io.path.inputStream -class ICRFTest : StringSpec() { +class ICRFTest { - init { - val iersa = IERSA() - iersa.load(Path.of("../data/finals2000A.all").inputStream()) - IERS.attach(iersa) + @Test + fun equatorialAtDateToEquatorialJ2000() { + val ra = 2.15105.deg + val dec = (-0.4493).deg + val (raNow, decNow) = ICRF.equatorial(ra, dec, epoch = TT(2459950.0, 0.24436)).equatorial() + raNow.toDegrees shouldBe (1.85881 plusOrMinus 1e-4) + decNow.toDegrees shouldBe (-0.5762 plusOrMinus 1e-4) + } - "equatorial at date to equatorial J2000" { - val ra = 2.15105.deg - val dec = (-0.4493).deg - val (raNow, decNow) = ICRF.equatorial(ra, dec, epoch = TT(2459950.0, 0.24436)).equatorial() - raNow.toDegrees shouldBe (1.85881 plusOrMinus 1e-4) - decNow.toDegrees shouldBe (-0.5762 plusOrMinus 1e-4) - } - "equatorial J2000 to equatorial at date" { - val ra = 1.85881.deg - val dec = (-0.5762).deg - val (raNow, decNow) = ICRF.equatorial(ra, dec).equatorialAtEpoch(TT(2459950.0, 0.24436)) - raNow.toDegrees shouldBe (2.15105 plusOrMinus 1e-4) - decNow.toDegrees shouldBe (-0.4493 plusOrMinus 1e-4) - } - "horizontal" { - // Sirius. - val ra = "06 45 08.91728".hours - val dec = "-16 42 58.0171".deg - - val latitude = (-23.547500000000003).deg - val longitude = (-46.63610833333333).deg - val elevation = 853.m - - val time = TimeYMDHMS(2023, 1, 30, 22) - val site = Geoid.IERS2010.lonLat(longitude, latitude, elevation) - - val icrf = ICRF.equatorial(ra, dec, time = time, center = site) - val azAlt = icrf.horizontal() - azAlt.longitude.normalized.toDegrees shouldBe (90.778 plusOrMinus 1e-1) - azAlt.latitude.toDegrees shouldBe (44.3538 plusOrMinus 1e-1) + @Test + fun equatorialJ2000ToEquatorialAtDate() { + val ra = 1.85881.deg + val dec = (-0.5762).deg + val (raNow, decNow) = ICRF.equatorial(ra, dec).equatorialAtEpoch(TT(2459950.0, 0.24436)) + raNow.toDegrees shouldBe (2.15105 plusOrMinus 1e-4) + decNow.toDegrees shouldBe (-0.4493 plusOrMinus 1e-4) + } + + @Test + fun horizontal() { + // Sirius. + val ra = "06 45 08.91728".hours + val dec = "-16 42 58.0171".deg + + val latitude = (-23.547500000000003).deg + val longitude = (-46.63610833333333).deg + val elevation = 853.m + + val time = TimeYMDHMS(2023, 1, 30, 22) + val site = Geoid.IERS2010.lonLat(longitude, latitude, elevation) + + val icrf = ICRF.equatorial(ra, dec, time = time, center = site) + val azAlt = icrf.horizontal() + azAlt.longitude.normalized.toDegrees shouldBe (90.778 plusOrMinus 1e-1) + azAlt.latitude.toDegrees shouldBe (44.3538 plusOrMinus 1e-1) + } + + companion object { + + @JvmStatic + @BeforeAll + fun loadIERS() { + val iersa = IERSA() + dataDirectory.concat("finals2000A.all").inputStream().use(iersa::load) + IERS.attach(iersa) } } } diff --git a/nebulosa-nova/src/test/kotlin/KeplerOrbitTest.kt b/nebulosa-nova/src/test/kotlin/KeplerOrbitTest.kt index 7c20f197d..186fce987 100644 --- a/nebulosa-nova/src/test/kotlin/KeplerOrbitTest.kt +++ b/nebulosa-nova/src/test/kotlin/KeplerOrbitTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeLessThan import io.kotest.matchers.shouldBe @@ -10,48 +9,50 @@ import nebulosa.nova.astrometry.Asteroid import nebulosa.nova.position.ICRF import nebulosa.time.TDB import nebulosa.time.TimeYMDHMS +import org.junit.jupiter.api.Test + +class KeplerOrbitTest { + + @Test + fun asteroidMpc() { + val ceres = + Asteroid.parse("00001 3.33 0.15 K232P 17.21569 73.47045 80.26013 10.58634 0.0788175 0.21411523 2.7671817 0 MPO719049 7258 123 1801-2022 0.65 M-v 30l MPCLINUX 0000 (1) Ceres 20220916") + val time = TDB(TimeYMDHMS(2022, 12, 15, 22, 0, 0.0)) + val icrf = ceres.at(time) + + icrf.position[0] shouldBe (-2.2887358680299896 plusOrMinus 1e-6) + icrf.position[1] shouldBe (0.7613682864183494 plusOrMinus 1e-6) + icrf.position[2] shouldBe (0.8249889493185274 plusOrMinus 1e-6) + icrf.velocity[0] shouldBe (-0.004522105973639024 plusOrMinus 1e-6) + icrf.velocity[1] shouldBe (-0.009582289706142557 plusOrMinus 1e-6) + icrf.velocity[2] shouldBe (-0.003598833220781664 plusOrMinus 1e-6) + } + + @Test + fun asteroidMeanAnomaly() { + val time = TDB(2458886.5) + + val ceres = Asteroid( + semiMajorAxis = 2.768873850275102.au, + eccentricity = 7.705857791518426E-02, + inclination = 2.718528770987308E+01.deg, + argumentOfPerihelion = 1.328964361683606E+02.deg, // W + longitudeOfAscendingNode = 2.336112629072238E+01.deg, // OM + meanAnomaly = 1.382501360489816E+02.deg, + epoch = time, + rotation = null, + ) + + val (r) = ceres.at(time) + + val sun = Vector3D(-0.004105894975783999, 0.006739680703224941, 0.002956344702049446) + val horizons = Vector3D(1.334875927366032E+00, -2.239607658161781E+00, -1.328895183461897E+00) + + val s = r + sun - horizons + val epsilon = 0.001.m -class KeplerOrbitTest : StringSpec() { - - init { - "asteroid: MPC" { - val ceres = - Asteroid.parse("00001 3.33 0.15 K232P 17.21569 73.47045 80.26013 10.58634 0.0788175 0.21411523 2.7671817 0 MPO719049 7258 123 1801-2022 0.65 M-v 30l MPCLINUX 0000 (1) Ceres 20220916") - val time = TDB(TimeYMDHMS(2022, 12, 15, 22, 0, 0.0)) - val icrf = ceres.at(time) - - icrf.position[0] shouldBe (-2.2887358680299896 plusOrMinus 1e-6) - icrf.position[1] shouldBe (0.7613682864183494 plusOrMinus 1e-6) - icrf.position[2] shouldBe (0.8249889493185274 plusOrMinus 1e-6) - icrf.velocity[0] shouldBe (-0.004522105973639024 plusOrMinus 1e-6) - icrf.velocity[1] shouldBe (-0.009582289706142557 plusOrMinus 1e-6) - icrf.velocity[2] shouldBe (-0.003598833220781664 plusOrMinus 1e-6) - } - "asteroid: mean anomaly" { - val time = TDB(2458886.5) - - val ceres = Asteroid( - semiMajorAxis = 2.768873850275102.au, - eccentricity = 7.705857791518426E-02, - inclination = 2.718528770987308E+01.deg, - argumentOfPerihelion = 1.328964361683606E+02.deg, // W - longitudeOfAscendingNode = 2.336112629072238E+01.deg, // OM - meanAnomaly = 1.382501360489816E+02.deg, - epoch = time, - rotation = null, - ) - - val (r) = ceres.at(time) - - val sun = Vector3D(-0.004105894975783999, 0.006739680703224941, 0.002956344702049446) - val horizons = Vector3D(1.334875927366032E+00, -2.239607658161781E+00, -1.328895183461897E+00) - - val s = r + sun - horizons - val epsilon = 0.001.m - - s[0] shouldBeLessThan epsilon - s[1] shouldBeLessThan epsilon - s[2] shouldBeLessThan epsilon - } + s[0] shouldBeLessThan epsilon + s[1] shouldBeLessThan epsilon + s[2] shouldBeLessThan epsilon } } diff --git a/nebulosa-nova/src/test/kotlin/SpiceKernelTest.kt b/nebulosa-nova/src/test/kotlin/SpiceKernelTest.kt index 5add77ec4..fe1fe1173 100644 --- a/nebulosa-nova/src/test/kotlin/SpiceKernelTest.kt +++ b/nebulosa-nova/src/test/kotlin/SpiceKernelTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.nasa.daf.RemoteDaf @@ -8,55 +7,63 @@ import nebulosa.nova.position.Barycentric import nebulosa.nova.position.ICRF import nebulosa.time.TDB import nebulosa.time.TimeYMDHMS +import org.junit.jupiter.api.Test -class SpiceKernelTest : StringSpec() { +class SpiceKernelTest { - init { - val time = TDB(TimeYMDHMS(2022, 11, 27, 22, 30, 0.0)) + @Test + fun ssbMarsBarycenter() { + val mars = MAR097[4] + val barycentric = mars.at(TIME) + barycentric.position[0] shouldBe (0.5001501370337544 plusOrMinus 1e-13) + barycentric.position[1] shouldBe (1.3081776387439241 plusOrMinus 1e-13) + barycentric.position[2] shouldBe (0.5865138637117445 plusOrMinus 1e-13) + barycentric.velocity[0] shouldBe (-0.012655073594243272 plusOrMinus 1e-13) + barycentric.velocity[1] shouldBe (0.005210405441841531 plusOrMinus 1e-13) + barycentric.velocity[2] shouldBe (0.0027316410426030074 plusOrMinus 1e-13) + } + + @Test + fun marsBarycenterMars() { + val mars = MAR097[499] - MAR097[4] + val icrf = mars.at(TIME) + icrf.position[0] shouldBe (-6.973204561568923e-13 plusOrMinus 1e-13) + icrf.position[1] shouldBe (-1.0364158902365925e-12 plusOrMinus 1e-13) + icrf.position[2] shouldBe (-1.3393210655347855e-13 plusOrMinus 1e-13) + icrf.velocity[0] shouldBe (1.7113987241274633e-11 plusOrMinus 1e-13) + icrf.velocity[1] shouldBe (-5.325181995164045e-12 plusOrMinus 1e-13) + icrf.velocity[2] shouldBe (-1.2236477437195075e-11 plusOrMinus 1e-13) + } + + @Test + fun positionOfMars() { + val mars = MAR097[499] + val barycentric = mars.at(TIME) + barycentric.position[0] shouldBe (0.5001501370330571 plusOrMinus 1e-13) + barycentric.position[1] shouldBe (1.3081776387428876 plusOrMinus 1e-13) + barycentric.position[2] shouldBe (0.5865138637116106 plusOrMinus 1e-13) + barycentric.velocity[0] shouldBe (-0.012655073577129285 plusOrMinus 1e-13) + barycentric.velocity[1] shouldBe (0.005210405436516349 plusOrMinus 1e-13) + barycentric.velocity[2] shouldBe (0.00273164103036653 plusOrMinus 1e-13) + } + + @Test + fun positionOfMarsViewedFromEarth() { + val earth = MAR097[399] + val mars = MAR097[499] + val barycentric = earth.at(TIME) + val astrometric = barycentric.observe(mars) + astrometric.position[0] shouldBe (0.09761625675629965 plusOrMinus 1e-13) + astrometric.position[1] shouldBe (0.48508891609539406 plusOrMinus 1e-13) + astrometric.position[2] shouldBe (0.22948292606924786 plusOrMinus 1e-13) + astrometric.velocity[0] shouldBe (0.003266059522699063 plusOrMinus 1e-13) + astrometric.velocity[1] shouldBe (-0.0013034648919245393 plusOrMinus 1e-13) + astrometric.velocity[2] shouldBe (-9.24056040658666e-05 plusOrMinus 1e-13) + } - val mar097 = SpiceKernel(Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/satellites/mar097.bsp"))) + companion object { - "SSB -> Mars barycenter" { - val mars = mar097[4] - val barycentric = mars.at(time) - barycentric.position[0] shouldBe (0.5001501370337544 plusOrMinus 1e-13) - barycentric.position[1] shouldBe (1.3081776387439241 plusOrMinus 1e-13) - barycentric.position[2] shouldBe (0.5865138637117445 plusOrMinus 1e-13) - barycentric.velocity[0] shouldBe (-0.012655073594243272 plusOrMinus 1e-13) - barycentric.velocity[1] shouldBe (0.005210405441841531 plusOrMinus 1e-13) - barycentric.velocity[2] shouldBe (0.0027316410426030074 plusOrMinus 1e-13) - } - "Mars Barycenter -> Mars" { - val mars = mar097[499] - mar097[4] - val icrf = mars.at(time) - icrf.position[0] shouldBe (-6.973204561568923e-13 plusOrMinus 1e-13) - icrf.position[1] shouldBe (-1.0364158902365925e-12 plusOrMinus 1e-13) - icrf.position[2] shouldBe (-1.3393210655347855e-13 plusOrMinus 1e-13) - icrf.velocity[0] shouldBe (1.7113987241274633e-11 plusOrMinus 1e-13) - icrf.velocity[1] shouldBe (-5.325181995164045e-12 plusOrMinus 1e-13) - icrf.velocity[2] shouldBe (-1.2236477437195075e-11 plusOrMinus 1e-13) - } - "position of mars" { - val mars = mar097[499] - val barycentric = mars.at(time) - barycentric.position[0] shouldBe (0.5001501370330571 plusOrMinus 1e-13) - barycentric.position[1] shouldBe (1.3081776387428876 plusOrMinus 1e-13) - barycentric.position[2] shouldBe (0.5865138637116106 plusOrMinus 1e-13) - barycentric.velocity[0] shouldBe (-0.012655073577129285 plusOrMinus 1e-13) - barycentric.velocity[1] shouldBe (0.005210405436516349 plusOrMinus 1e-13) - barycentric.velocity[2] shouldBe (0.00273164103036653 plusOrMinus 1e-13) - } - "position of mars viewed from earth" { - val earth = mar097[399] - val mars = mar097[499] - val barycentric = earth.at(time) - val astrometric = barycentric.observe(mars) - astrometric.position[0] shouldBe (0.09761625675629965 plusOrMinus 1e-13) - astrometric.position[1] shouldBe (0.48508891609539406 plusOrMinus 1e-13) - astrometric.position[2] shouldBe (0.22948292606924786 plusOrMinus 1e-13) - astrometric.velocity[0] shouldBe (0.003266059522699063 plusOrMinus 1e-13) - astrometric.velocity[1] shouldBe (-0.0013034648919245393 plusOrMinus 1e-13) - astrometric.velocity[2] shouldBe (-9.24056040658666e-05 plusOrMinus 1e-13) - } + @JvmStatic private val TIME = TDB(TimeYMDHMS(2022, 11, 27, 22, 30, 0.0)) + @JvmStatic private val MAR097 = SpiceKernel(Spk(RemoteDaf("https://naif.jpl.nasa.gov/pub/naif/generic_kernels/spk/satellites/mar097.bsp"))) } } diff --git a/nebulosa-nova/src/test/kotlin/VSOP87ETest.kt b/nebulosa-nova/src/test/kotlin/VSOP87ETest.kt index 1d6119fa0..a8c2b1c55 100644 --- a/nebulosa-nova/src/test/kotlin/VSOP87ETest.kt +++ b/nebulosa-nova/src/test/kotlin/VSOP87ETest.kt @@ -1,111 +1,126 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.nova.astrometry.VSOP87E import nebulosa.time.TDB import nebulosa.time.TimeJD +import org.junit.jupiter.api.Test -class VSOP87ETest : StringSpec() { +class VSOP87ETest { - init { - "sun" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.SUN.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (-9.061436632282236E-03 plusOrMinus 1e-5) - p[1] shouldBe (6.152584183942633E-05 plusOrMinus 1e-6) - p[2] shouldBe (2.553315997818977E-04 plusOrMinus 1e-6) - v[0] shouldBe (8.523732347654815E-07 plusOrMinus 1e-9) - v[1] shouldBe (-8.293160642939413E-06 plusOrMinus 1e-9) - v[2] shouldBe (-3.536408207768117E-06 plusOrMinus 1e-9) - } - "mercury" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.MERCURY.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (2.917749477051785E-01 plusOrMinus 1e-5) - p[1] shouldBe (1.307753573063683E-01 plusOrMinus 1e-5) - p[2] shouldBe (3.890188068703725E-02 plusOrMinus 1e-5) - v[0] shouldBe (-1.701508873456466E-02 plusOrMinus 1e-8) - v[1] shouldBe (2.317979796647154E-02 plusOrMinus 1e-8) - v[2] shouldBe (1.414717965470348E-02 plusOrMinus 1e-8) - } - "venus" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.VENUS.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (4.530231016612877E-01 plusOrMinus 1e-5) - p[1] shouldBe (-5.016891998928698E-01 plusOrMinus 1e-5) - p[2] shouldBe (-2.547479296682298E-01 plusOrMinus 1e-5) - v[0] shouldBe (1.548765426376424E-02 plusOrMinus 1e-7) - v[1] shouldBe (1.199990886845585E-02 plusOrMinus 1e-7) - v[2] shouldBe (4.419843673733974E-03 plusOrMinus 1e-7) - } - "earth" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.EARTH.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (-5.774546457978428E-02 plusOrMinus 1e-5) - p[1] shouldBe (9.014209322230198E-01 plusOrMinus 1e-5) - p[2] shouldBe (3.909900074777803E-01 plusOrMinus 1e-5) - v[0] shouldBe (-1.746880754444190E-02 plusOrMinus 1e-7) - v[1] shouldBe (-8.523045001854702E-04 plusOrMinus 1e-7) - v[2] shouldBe (-3.690873763513220E-04 plusOrMinus 1e-7) - } - "mars" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.MARS.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (1.458124773200954E-01 plusOrMinus 1e-5) - p[1] shouldBe (1.408612236938829E+00 plusOrMinus 1e-5) - p[2] shouldBe (6.421493185701298E-01 plusOrMinus 1e-5) - v[0] shouldBe (-1.339320293960258E-02 plusOrMinus 1e-7) - v[1] shouldBe (2.208863862273841E-03 plusOrMinus 1e-7) - v[2] shouldBe (1.374808996076603E-03 plusOrMinus 1e-7) - } - "jupiter" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.JUPITER.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (4.840655361543647E+00 plusOrMinus 1e-5) - p[1] shouldBe (9.540894823613197E-01 plusOrMinus 1e-5) - p[2] shouldBe (2.911277391212874E-01 plusOrMinus 1e-5) - v[0] shouldBe (-1.599598915586027E-03 plusOrMinus 1e-7) - v[1] shouldBe (7.106125606377319E-03 plusOrMinus 1e-6) - v[2] shouldBe (3.084863409738924E-03 plusOrMinus 1e-7) - } - "saturn" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.SATURN.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (8.118575220877252E+00 plusOrMinus 1e-5) - p[1] shouldBe (-4.990053770638640E+00 plusOrMinus 1e-5) - p[2] shouldBe (-2.410825392716150E+00 plusOrMinus 1e-5) - v[0] shouldBe (2.830471313637587E-03 plusOrMinus 1e-6) - v[1] shouldBe (4.295334526830042E-03 plusOrMinus 1e-6) - v[2] shouldBe (1.652340036885691E-03 plusOrMinus 1e-6) - } - "uranus" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.URANUS.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (1.338107802611238E+01 plusOrMinus 1e-4) - p[1] shouldBe (1.326832905623722E+01 plusOrMinus 1e-4) - p[2] shouldBe (5.621910507435053E+00 plusOrMinus 1e-4) - v[0] shouldBe (-2.910912382282302E-03 plusOrMinus 1e-7) - v[1] shouldBe (2.268725951577151E-03 plusOrMinus 1e-7) - v[2] shouldBe (1.034738977414487E-03 plusOrMinus 1e-7) - } - "neptune" { - val time = TDB(TimeJD(2459938.0, 0.5)) - val (p, v) = VSOP87E.NEPTUNE.compute(time) - // https://ssd.jpl.nasa.gov/horizons/app.html#/ - p[0] shouldBe (2.974978125776706E+01 plusOrMinus 1e-4) - p[1] shouldBe (-2.471636747869995E+00 plusOrMinus 1e-3) - p[2] shouldBe (-1.752319118165945E+00 plusOrMinus 1e-4) - v[0] shouldBe (2.908617762745452E-04 plusOrMinus 1e-6) - v[1] shouldBe (2.911615062634410E-03 plusOrMinus 1e-6) - v[2] shouldBe (1.184401587981311E-03 plusOrMinus 1e-6) - } + @Test + fun sun() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.SUN.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (-9.061436632282236E-03 plusOrMinus 1e-5) + p[1] shouldBe (6.152584183942633E-05 plusOrMinus 1e-6) + p[2] shouldBe (2.553315997818977E-04 plusOrMinus 1e-6) + v[0] shouldBe (8.523732347654815E-07 plusOrMinus 1e-9) + v[1] shouldBe (-8.293160642939413E-06 plusOrMinus 1e-9) + v[2] shouldBe (-3.536408207768117E-06 plusOrMinus 1e-9) + } + + @Test + fun mercury() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.MERCURY.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (2.917749477051785E-01 plusOrMinus 1e-5) + p[1] shouldBe (1.307753573063683E-01 plusOrMinus 1e-5) + p[2] shouldBe (3.890188068703725E-02 plusOrMinus 1e-5) + v[0] shouldBe (-1.701508873456466E-02 plusOrMinus 1e-8) + v[1] shouldBe (2.317979796647154E-02 plusOrMinus 1e-8) + v[2] shouldBe (1.414717965470348E-02 plusOrMinus 1e-8) + } + + @Test + fun venus() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.VENUS.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (4.530231016612877E-01 plusOrMinus 1e-5) + p[1] shouldBe (-5.016891998928698E-01 plusOrMinus 1e-5) + p[2] shouldBe (-2.547479296682298E-01 plusOrMinus 1e-5) + v[0] shouldBe (1.548765426376424E-02 plusOrMinus 1e-7) + v[1] shouldBe (1.199990886845585E-02 plusOrMinus 1e-7) + v[2] shouldBe (4.419843673733974E-03 plusOrMinus 1e-7) + } + + @Test + fun earth() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.EARTH.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (-5.774546457978428E-02 plusOrMinus 1e-5) + p[1] shouldBe (9.014209322230198E-01 plusOrMinus 1e-5) + p[2] shouldBe (3.909900074777803E-01 plusOrMinus 1e-5) + v[0] shouldBe (-1.746880754444190E-02 plusOrMinus 1e-7) + v[1] shouldBe (-8.523045001854702E-04 plusOrMinus 1e-7) + v[2] shouldBe (-3.690873763513220E-04 plusOrMinus 1e-7) + } + + @Test + fun mars() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.MARS.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (1.458124773200954E-01 plusOrMinus 1e-5) + p[1] shouldBe (1.408612236938829E+00 plusOrMinus 1e-5) + p[2] shouldBe (6.421493185701298E-01 plusOrMinus 1e-5) + v[0] shouldBe (-1.339320293960258E-02 plusOrMinus 1e-7) + v[1] shouldBe (2.208863862273841E-03 plusOrMinus 1e-7) + v[2] shouldBe (1.374808996076603E-03 plusOrMinus 1e-7) + } + + @Test + fun jupiter() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.JUPITER.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (4.840655361543647E+00 plusOrMinus 1e-5) + p[1] shouldBe (9.540894823613197E-01 plusOrMinus 1e-5) + p[2] shouldBe (2.911277391212874E-01 plusOrMinus 1e-5) + v[0] shouldBe (-1.599598915586027E-03 plusOrMinus 1e-7) + v[1] shouldBe (7.106125606377319E-03 plusOrMinus 1e-6) + v[2] shouldBe (3.084863409738924E-03 plusOrMinus 1e-7) + } + + @Test + fun saturn() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.SATURN.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (8.118575220877252E+00 plusOrMinus 1e-5) + p[1] shouldBe (-4.990053770638640E+00 plusOrMinus 1e-5) + p[2] shouldBe (-2.410825392716150E+00 plusOrMinus 1e-5) + v[0] shouldBe (2.830471313637587E-03 plusOrMinus 1e-6) + v[1] shouldBe (4.295334526830042E-03 plusOrMinus 1e-6) + v[2] shouldBe (1.652340036885691E-03 plusOrMinus 1e-6) + } + + @Test + fun uranus() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.URANUS.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (1.338107802611238E+01 plusOrMinus 1e-4) + p[1] shouldBe (1.326832905623722E+01 plusOrMinus 1e-4) + p[2] shouldBe (5.621910507435053E+00 plusOrMinus 1e-4) + v[0] shouldBe (-2.910912382282302E-03 plusOrMinus 1e-7) + v[1] shouldBe (2.268725951577151E-03 plusOrMinus 1e-7) + v[2] shouldBe (1.034738977414487E-03 plusOrMinus 1e-7) + } + + @Test + fun neptune() { + val time = TDB(TimeJD(2459938.0, 0.5)) + val (p, v) = VSOP87E.NEPTUNE.compute(time) + // https://ssd.jpl.nasa.gov/horizons/app.html#/ + p[0] shouldBe (2.974978125776706E+01 plusOrMinus 1e-4) + p[1] shouldBe (-2.471636747869995E+00 plusOrMinus 1e-3) + p[2] shouldBe (-1.752319118165945E+00 plusOrMinus 1e-4) + v[0] shouldBe (2.908617762745452E-04 plusOrMinus 1e-6) + v[1] shouldBe (2.911615062634410E-03 plusOrMinus 1e-6) + v[2] shouldBe (1.184401587981311E-03 plusOrMinus 1e-6) } } diff --git a/nebulosa-phd2-client/src/test/kotlin/PHD2ClientTest.kt b/nebulosa-phd2-client/src/test/kotlin/PHD2ClientTest.kt index 6505bd8f8..ceb9da26e 100644 --- a/nebulosa-phd2-client/src/test/kotlin/PHD2ClientTest.kt +++ b/nebulosa-phd2-client/src/test/kotlin/PHD2ClientTest.kt @@ -1,25 +1,22 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec -import kotlinx.coroutines.delay import nebulosa.phd2.client.PHD2Client import nebulosa.phd2.client.PHD2EventListener import nebulosa.phd2.client.commands.PHD2Command import nebulosa.phd2.client.events.PHD2Event -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class PHD2ClientTest : StringSpec(), PHD2EventListener { +@NonGitHubOnly +class PHD2ClientTest : PHD2EventListener { - init { - "start" { - val client = PHD2Client() - client.registerListener(this@PHD2ClientTest) - client.open("localhost", PHD2Client.DEFAULT_PORT) + @Test + fun start() { + val client = PHD2Client() + client.registerListener(this@PHD2ClientTest) + client.open("localhost", PHD2Client.DEFAULT_PORT) - delay(1000) + Thread.sleep(1000) - client.close() - } + client.close() } override fun onEventReceived(event: PHD2Event) { diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt index e4c6a0feb..f3590870d 100644 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightAutoStackerTest.kt @@ -1,42 +1,46 @@ import PixInsightScriptTest.Companion.openAsImage -import io.kotest.core.annotation.EnabledIf -import io.kotest.engine.spec.tempdir -import io.kotest.engine.spec.tempfile import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.shouldBe import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction import nebulosa.pixinsight.script.PixInsightScriptRunner import nebulosa.pixinsight.stacker.PixInsightAutoStacker -import nebulosa.test.AbstractFitsAndXisfTest -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.* +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test import java.nio.file.Path -@EnabledIf(NonGitHubOnlyCondition::class) -class PixInsightAutoStackerTest : AbstractFitsAndXisfTest() { +@NonGitHubOnly +class PixInsightAutoStackerTest : AbstractTest() { - init { - val runner = PixInsightScriptRunner(Path.of("PixInsight")) - val workingDirectory = tempdir("pi-").toPath() + @Test + fun stack() { + val files = listOf(PI_01_LIGHT, PI_02_LIGHT, PI_03_LIGHT, PI_04_LIGHT, PI_05_LIGHT, PI_06_LIGHT, PI_07_LIGHT, PI_08_LIGHT) + val workingDirectory = tempDirectory("pi-") + val outputPath = tempPath("pi-", ".fits") - "stack" { - val files = listOf(PI_01_LIGHT, PI_02_LIGHT, PI_03_LIGHT, PI_04_LIGHT, PI_05_LIGHT, PI_06_LIGHT, PI_07_LIGHT, PI_08_LIGHT) - val outputPath = tempfile("pi-", ".fits").toPath() + val stacker = PixInsightAutoStacker(RUNNER, workingDirectory) + stacker.stack(files, outputPath).shouldBeTrue() - val stacker = PixInsightAutoStacker(runner, workingDirectory) - stacker.stack(files, outputPath).shouldBeTrue() + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-auto-stacked").second shouldBe "a107143dff3d43c4b56c872da869f89b" + } + + @Test + @Disabled + fun calibratedStack() { + val files = listOf(PI_01_LIGHT, PI_02_LIGHT, PI_03_LIGHT, PI_04_LIGHT, PI_05_LIGHT, PI_06_LIGHT, PI_07_LIGHT, PI_08_LIGHT) + val workingDirectory = tempDirectory("pi-") + val outputPath = tempPath("pi-", ".fits") - outputPath.openAsImage().transform(AutoScreenTransformFunction) - .save("pi-auto-stacked").second shouldBe "a107143dff3d43c4b56c872da869f89b" - } - "!calibrated stack" { - val files = listOf(PI_01_LIGHT, PI_02_LIGHT, PI_03_LIGHT, PI_04_LIGHT, PI_05_LIGHT, PI_06_LIGHT, PI_07_LIGHT, PI_08_LIGHT) - val outputPath = tempfile("pi-", ".fits").toPath() + val stacker = PixInsightAutoStacker(RUNNER, workingDirectory, PI_DARK, PI_FLAT, PI_BIAS) + stacker.stack(files, outputPath).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-calibrated-auto-stacked").second shouldBe "" + } - val stacker = PixInsightAutoStacker(runner, workingDirectory, PI_DARK, PI_FLAT, PI_BIAS) - stacker.stack(files, outputPath).shouldBeTrue() + companion object { - outputPath.openAsImage().transform(AutoScreenTransformFunction) - .save("pi-calibrated-auto-stacked").second shouldBe "" - } + @JvmStatic private val RUNNER = PixInsightScriptRunner(Path.of("PixInsight")) } } diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightPlateSolverTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightPlateSolverTest.kt index 52794a061..79349cd4d 100644 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightPlateSolverTest.kt +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightPlateSolverTest.kt @@ -1,37 +1,37 @@ -import io.kotest.core.annotation.EnabledIf import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe import nebulosa.math.* import nebulosa.pixinsight.platesolver.PixInsightPlateSolver import nebulosa.pixinsight.script.PixInsightScriptRunner -import nebulosa.test.AbstractFitsAndXisfTest -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.AbstractTest +import nebulosa.test.NonGitHubOnly +import nebulosa.test.download +import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.math.roundToInt -@EnabledIf(NonGitHubOnlyCondition::class) -class PixInsightPlateSolverTest : AbstractFitsAndXisfTest() { +@NonGitHubOnly +class PixInsightPlateSolverTest : AbstractTest() { - init { - "solver" { - val runner = PixInsightScriptRunner(Path.of("PixInsight")) - val pixelSize = 6.58 - val resolution = 6.78.arcsec - val solver = PixInsightPlateSolver(runner, pixelSize, resolution = resolution) - val path = download("https://nova.astrometry.net/image/14603", "jpg") - val centerRA = "06 40 51".hours - val centerDEC = "09 49 53".deg + @Test + fun solver() { + val runner = PixInsightScriptRunner(Path.of("PixInsight")) + val pixelSize = 6.58 + val resolution = 6.78.arcsec + val solver = PixInsightPlateSolver(runner, pixelSize, resolution = resolution) + val path = download("https://nova.astrometry.net/image/14603", "jpg") + val centerRA = "06 40 51".hours + val centerDEC = "09 49 53".deg - val solution = solver.solve(path, null, centerRA, centerDEC) + val solution = solver.solve(path, null, centerRA, centerDEC) - solution.scale.toArcsec shouldBe (6.774 plusOrMinus 1e-3) - solution.rightAscension.formatHMS() shouldBe "06h40m51.8s" - solution.declination.formatSignedDMS() shouldBe "+009°49'53.6\"" - solution.width.toArcmin shouldBe (90.321 plusOrMinus 1e-3) - solution.height.toArcmin shouldBe (59.386 plusOrMinus 1e-3) - solution.widthInPixels.roundToInt() shouldBeExactly 800 - solution.heightInPixels.roundToInt() shouldBeExactly 526 - } + solution.scale.toArcsec shouldBe (6.774 plusOrMinus 1e-3) + solution.rightAscension.formatHMS() shouldBe "06h40m51.8s" + solution.declination.formatSignedDMS() shouldBe "+009°49'53.6\"" + solution.width.toArcmin shouldBe (90.321 plusOrMinus 1e-3) + solution.height.toArcmin shouldBe (59.386 plusOrMinus 1e-3) + solution.widthInPixels.roundToInt() shouldBeExactly 800 + solution.heightInPixels.roundToInt() shouldBeExactly 526 } } diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightScriptTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightScriptTest.kt index 2e61a9a2d..1af1ee98b 100644 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightScriptTest.kt +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightScriptTest.kt @@ -1,6 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.engine.spec.tempdir -import io.kotest.engine.spec.tempfile import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.ints.shouldBeExactly @@ -8,175 +5,194 @@ import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import nebulosa.fits.fits import nebulosa.fits.isFits -import nebulosa.image.Image +import nebulosa.image.Image.Companion.asImage import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction import nebulosa.math.* import nebulosa.pixinsight.script.* import nebulosa.pixinsight.script.PixInsightScript.Companion.UNSPECIFIED_SLOT -import nebulosa.test.AbstractFitsAndXisfTest -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.* import nebulosa.xisf.isXisf import nebulosa.xisf.xisf +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.math.roundToInt -@EnabledIf(NonGitHubOnlyCondition::class) -class PixInsightScriptTest : AbstractFitsAndXisfTest() { +@NonGitHubOnly +class PixInsightScriptTest : AbstractTest() { - init { - val runner = PixInsightScriptRunner(Path.of("PixInsight")) - val workingDirectory = tempdir("pi-").toPath() + @Test + @Disabled + fun startup() { + PixInsightStartup(PixInsightScript.DEFAULT_SLOT) + .use { it.runSync(RUNNER).success.shouldBeTrue() } + } - "!startup" { - PixInsightStartup(PixInsightScript.DEFAULT_SLOT) - .use { it.runSync(runner).success.shouldBeTrue() } - } - "!is running" { - PixInsightIsRunning(PixInsightScript.DEFAULT_SLOT) - .use { it.runSync(runner).success.shouldBeTrue() } - } - "calibrate" { - PixInsightCalibrate(UNSPECIFIED_SLOT, workingDirectory, PI_01_LIGHT, PI_DARK, PI_FLAT, PI_BIAS) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-calibrate").second shouldBe "731562ee12f45bf7c1095f4773f70e71" - } - "align" { - PixInsightAlign(UNSPECIFIED_SLOT, workingDirectory, PI_01_LIGHT, PI_02_LIGHT) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-align").second shouldBe "483ebaf15afa5957fe099f3ee2beff78" - } - "detect stars" { - PixInsightDetectStars(UNSPECIFIED_SLOT, PI_FOCUS_0) - .use { it.runSync(runner).stars } - .map { it.hfd } - .average() shouldBe (8.43 plusOrMinus 1e-2) - - PixInsightDetectStars(UNSPECIFIED_SLOT, PI_FOCUS_30000) - .use { it.runSync(runner).stars } - .map { it.hfd } - .average() shouldBe (1.85 plusOrMinus 1e-2) - - PixInsightDetectStars(UNSPECIFIED_SLOT, PI_FOCUS_100000) - .use { it.runSync(runner).stars } - .map { it.hfd } - .average() shouldBe (18.35 plusOrMinus 1e-2) - } - "pixel math" { - val outputPath = tempfile("pi-stacked-", ".fits").toPath() - PixInsightPixelMath(UNSPECIFIED_SLOT, listOf(PI_01_LIGHT, PI_02_LIGHT), outputPath, "{{0}} + {{1}}") - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-pixelmath").second shouldBe "cafc8138e2ce17614dcfa10edf410b07" - } - "abe" { - val outputPath = tempfile("pi-abe-", ".fits").toPath() - PixInsightAutomaticBackgroundExtractor(UNSPECIFIED_SLOT, PI_01_LIGHT, outputPath) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-abe").second shouldBe "bf62207dc17190009ba215da7c011297" - } - "lrgb combination" { - val outputPath = tempfile("pi-lrgb-", ".fits").toPath() - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-lrgb").second shouldBe "99db35d78f7b360e7592217f4179b189" - - val weights = doubleArrayOf(1.0, 0.2470588, 0.31764705, 0.709803921) // LRGB #3F51B5 - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, weights) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-weighted-lrgb").second shouldBe "1148ee222fbfb382ad2d708df5b0f79f" - - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, null, null) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-lr").second shouldBe "9100d3ce892f05f4b832b2fb5f35b5a1" - - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, null, PI_01_LIGHT, null) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-lg").second shouldBe "b4e8d8f7e289db60b41ba2bbe0035344" - - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, null, null, PI_01_LIGHT) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-lb").second shouldBe "1760e7cb1d139b63022dd975fe84897d" - - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, PI_01_LIGHT, PI_01_LIGHT, null) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-rg").second shouldBe "8c59307b5943932aefdf2dedfe1c8178" - - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, PI_01_LIGHT, null, PI_01_LIGHT) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-rb").second shouldBe "1bdf9cada6a33f76dceaccdaacf30fef" - - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, null, PI_01_LIGHT, PI_01_LIGHT) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-bg").second shouldBe "4a9c81c71fd37546fd300d1037742fa2" - - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, null) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-lrg").second shouldBe "06c32c8679d409302423baa3a07fb241" - - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, null, PI_01_LIGHT) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-lrb").second shouldBe "f6d026cb63f7a58fc325e422c277ff89" - - PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, null, PI_01_LIGHT, PI_01_LIGHT) - .use { it.runSync(runner).outputImage.shouldNotBeNull().openAsImage() } - .transform(AutoScreenTransformFunction).save("pi-lbg").second shouldBe "67f961110fb4b9f0033b3b8dbc8b1638" - } - "file format conversion" { - val xisfPath = tempfile("pi-ffc", ".xisf").toPath() - PixInsightFileFormatConversion(UNSPECIFIED_SLOT, PI_01_LIGHT, xisfPath) - .use { it.runSync(runner).outputImage.shouldNotBeNull().isXisf().shouldBeTrue() } - - val fitsPath = tempfile("pi-ffc", ".fits").toPath() - PixInsightFileFormatConversion(UNSPECIFIED_SLOT, xisfPath, fitsPath) - .use { it.runSync(runner).outputImage.shouldNotBeNull().isFits().shouldBeTrue() } + @Test + @Disabled + fun isRunning() { + PixInsightIsRunning(PixInsightScript.DEFAULT_SLOT) + .use { it.runSync(RUNNER).success.shouldBeTrue() } + } + + @Test + fun calibrate() { + val workingDirectory = tempDirectory("pi-") + PixInsightCalibrate(UNSPECIFIED_SLOT, workingDirectory, PI_01_LIGHT, PI_DARK, PI_FLAT, PI_BIAS) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-calibrate").second shouldBe "731562ee12f45bf7c1095f4773f70e71" + } + + @Test + fun align() { + val workingDirectory = tempDirectory("pi-") + PixInsightAlign(UNSPECIFIED_SLOT, workingDirectory, PI_01_LIGHT, PI_02_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-align").second shouldBe "483ebaf15afa5957fe099f3ee2beff78" + } + + @Test + fun detectStars() { + PixInsightDetectStars(UNSPECIFIED_SLOT, PI_FOCUS_0) + .use { it.runSync(RUNNER).stars } + .map { it.hfd } + .average() shouldBe (8.43 plusOrMinus 1e-2) + + PixInsightDetectStars(UNSPECIFIED_SLOT, PI_FOCUS_30000) + .use { it.runSync(RUNNER).stars } + .map { it.hfd } + .average() shouldBe (1.85 plusOrMinus 1e-2) + + PixInsightDetectStars(UNSPECIFIED_SLOT, PI_FOCUS_100000) + .use { it.runSync(RUNNER).stars } + .map { it.hfd } + .average() shouldBe (18.35 plusOrMinus 1e-2) + } + + @Test + fun pixelMath() { + val outputPath = tempPath("pi-stacked-", ".fits") + PixInsightPixelMath(UNSPECIFIED_SLOT, listOf(PI_01_LIGHT, PI_02_LIGHT), outputPath, "{{0}} + {{1}}") + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-pixelmath").second shouldBe "cafc8138e2ce17614dcfa10edf410b07" + } + + @Test + fun abe() { + val outputPath = tempPath("pi-abe-", ".fits") + PixInsightAutomaticBackgroundExtractor(UNSPECIFIED_SLOT, PI_01_LIGHT, outputPath) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-abe").second shouldBe "bf62207dc17190009ba215da7c011297" + } + + @Test + fun lrgbCombination() { + val outputPath = tempPath("pi-lrgb-", ".fits") + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lrgb").second shouldBe "99db35d78f7b360e7592217f4179b189" + + val weights = doubleArrayOf(1.0, 0.2470588, 0.31764705, 0.709803921) // LRGB #3F51B5 + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, weights) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-weighted-lrgb").second shouldBe "1148ee222fbfb382ad2d708df5b0f79f" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, null, null) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lr").second shouldBe "9100d3ce892f05f4b832b2fb5f35b5a1" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, null, PI_01_LIGHT, null) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lg").second shouldBe "b4e8d8f7e289db60b41ba2bbe0035344" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, null, null, PI_01_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lb").second shouldBe "1760e7cb1d139b63022dd975fe84897d" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, PI_01_LIGHT, PI_01_LIGHT, null) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-rg").second shouldBe "8c59307b5943932aefdf2dedfe1c8178" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, PI_01_LIGHT, null, PI_01_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-rb").second shouldBe "1bdf9cada6a33f76dceaccdaacf30fef" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, null, null, PI_01_LIGHT, PI_01_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-bg").second shouldBe "4a9c81c71fd37546fd300d1037742fa2" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, PI_01_LIGHT, null) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lrg").second shouldBe "06c32c8679d409302423baa3a07fb241" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, PI_01_LIGHT, null, PI_01_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lrb").second shouldBe "f6d026cb63f7a58fc325e422c277ff89" + + PixInsightLRGBCombination(UNSPECIFIED_SLOT, outputPath, PI_01_LIGHT, null, PI_01_LIGHT, PI_01_LIGHT) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().openAsImage() } + .transform(AutoScreenTransformFunction).save("pi-lbg").second shouldBe "67f961110fb4b9f0033b3b8dbc8b1638" + } + + @Test + fun fileFormatConversion() { + val xisfPath = tempPath("pi-ffc", ".xisf") + PixInsightFileFormatConversion(UNSPECIFIED_SLOT, PI_01_LIGHT, xisfPath) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().isXisf().shouldBeTrue() } + + val fitsPath = tempPath("pi-ffc", ".fits") + PixInsightFileFormatConversion(UNSPECIFIED_SLOT, xisfPath, fitsPath) + .use { it.runSync(RUNNER).outputImage.shouldNotBeNull().isFits().shouldBeTrue() } + } + + @Test + fun imageSolver() { + // https://nova.astrometry.net/user_images/10373761 + val path = download("https://nova.astrometry.net/image/14603", "jpg") + val resolution = 6.78 // arcsec/px + val centerRA = "06 40 51".hours + val centerDEC = "09 49 53".deg + + // Estimated to match resolution = (pixelSize / focalDistance) * 206.265 + val focalDistance = 200.0 // mm + val pixelSize = 6.58 + + with(PixInsightImageSolver(UNSPECIFIED_SLOT, path, centerRA, centerDEC, pixelSize = pixelSize, resolution = resolution) + .use { it.runSync(RUNNER) }) { + success.shouldBeTrue() + this.focalLength shouldBe (200.355 plusOrMinus 1e-5) + this.pixelSize shouldBe (6.58 plusOrMinus 1e-2) + this.resolution.toArcsec shouldBe (6.774 plusOrMinus 1e-3) + rightAscension.formatHMS() shouldBe "06h40m51.8s" + declination.formatSignedDMS() shouldBe "+009°49'53.6\"" + width.toArcmin shouldBe (90.321 plusOrMinus 1e-3) + height.toArcmin shouldBe (59.386 plusOrMinus 1e-3) + imageWidth.roundToInt() shouldBeExactly 800 + imageHeight.roundToInt() shouldBeExactly 526 } - "image solver" { - // https://nova.astrometry.net/user_images/10373761 - val path = download("https://nova.astrometry.net/image/14603", "jpg") - val resolution = 6.78 // arcsec/px - val centerRA = "06 40 51".hours - val centerDEC = "09 49 53".deg - - // Estimated to match resolution = (pixelSize / focalDistance) * 206.265 - val focalDistance = 200.0 // mm - val pixelSize = 6.58 - - with(PixInsightImageSolver(UNSPECIFIED_SLOT, path, centerRA, centerDEC, pixelSize = pixelSize, resolution = resolution) - .use { it.runSync(runner) }) { - success.shouldBeTrue() - this.focalLength shouldBe (200.355 plusOrMinus 1e-5) - this.pixelSize shouldBe (6.58 plusOrMinus 1e-2) - this.resolution.toArcsec shouldBe (6.774 plusOrMinus 1e-3) - rightAscension.formatHMS() shouldBe "06h40m51.8s" - declination.formatSignedDMS() shouldBe "+009°49'53.6\"" - width.toArcmin shouldBe (90.321 plusOrMinus 1e-3) - height.toArcmin shouldBe (59.386 plusOrMinus 1e-3) - imageWidth.roundToInt() shouldBeExactly 800 - imageHeight.roundToInt() shouldBeExactly 526 - } - - with(PixInsightImageSolver(UNSPECIFIED_SLOT, path, centerRA, centerDEC, pixelSize = pixelSize, focalLength = focalDistance) - .use { it.runSync(runner) }) { - success.shouldBeTrue() - this.focalLength shouldBe (200.355 plusOrMinus 1e-5) - this.pixelSize shouldBe (6.58 plusOrMinus 1e-2) - this.resolution.toArcsec shouldBe (6.774 plusOrMinus 1e-3) - rightAscension.formatHMS() shouldBe "06h40m51.8s" - declination.formatSignedDMS() shouldBe "+009°49'53.6\"" - width.toArcmin shouldBe (90.321 plusOrMinus 1e-3) - height.toArcmin shouldBe (59.386 plusOrMinus 1e-3) - imageWidth.roundToInt() shouldBeExactly 800 - imageHeight.roundToInt() shouldBeExactly 526 - } + + with(PixInsightImageSolver(UNSPECIFIED_SLOT, path, centerRA, centerDEC, pixelSize = pixelSize, focalLength = focalDistance) + .use { it.runSync(RUNNER) }) { + success.shouldBeTrue() + this.focalLength shouldBe (200.355 plusOrMinus 1e-5) + this.pixelSize shouldBe (6.58 plusOrMinus 1e-2) + this.resolution.toArcsec shouldBe (6.774 plusOrMinus 1e-3) + rightAscension.formatHMS() shouldBe "06h40m51.8s" + declination.formatSignedDMS() shouldBe "+009°49'53.6\"" + width.toArcmin shouldBe (90.321 plusOrMinus 1e-3) + height.toArcmin shouldBe (59.386 plusOrMinus 1e-3) + imageWidth.roundToInt() shouldBeExactly 800 + imageHeight.roundToInt() shouldBeExactly 526 } } companion object { + @JvmStatic private val RUNNER = PixInsightScriptRunner(Path.of("PixInsight")) + @JvmStatic - internal fun Path.openAsImage(): Image { - return if (isFits()) fits().use(Image::open) - else if (isXisf()) xisf().use(Image::open) - else throw IllegalArgumentException("the path at $this is not an image") - } + internal fun Path.openAsImage() = if (isFits()) fits().asImage() + else if (isXisf()) xisf().asImage() + else throw IllegalArgumentException("the path at $this is not an image") } } diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightStackerTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightStackerTest.kt index 1a8574bb2..e912c2457 100644 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightStackerTest.kt +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightStackerTest.kt @@ -1,58 +1,73 @@ import PixInsightScriptTest.Companion.openAsImage -import io.kotest.core.annotation.EnabledIf -import io.kotest.engine.spec.tempdir -import io.kotest.engine.spec.tempfile import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.shouldBe import nebulosa.image.algorithms.transformation.AutoScreenTransformFunction import nebulosa.pixinsight.script.PixInsightScriptRunner import nebulosa.pixinsight.stacker.PixInsightStacker -import nebulosa.test.AbstractFitsAndXisfTest -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.* +import org.junit.jupiter.api.Test import java.nio.file.Path -@EnabledIf(NonGitHubOnlyCondition::class) -class PixInsightStackerTest : AbstractFitsAndXisfTest() { - - init { - val runner = PixInsightScriptRunner(Path.of("PixInsight")) - val workingDirectory = tempdir("pi-").toPath() - val stacker = PixInsightStacker(runner, workingDirectory) - - "align" { - val outputPath = tempfile("pi-", ".fits").toPath() - stacker.align(PI_01_LIGHT, PI_03_LIGHT, outputPath).shouldBeTrue() - - outputPath.openAsImage().transform(AutoScreenTransformFunction) - .save("pi-aligned").second shouldBe "106651a7c1e640852384284ec12e0977" - } - "calibrate" { - val outputPath = tempfile("pi-", ".fits").toPath() - stacker.calibrate(PI_01_LIGHT, outputPath, PI_DARK).shouldBeTrue() - - outputPath.openAsImage().transform(AutoScreenTransformFunction) - .save("pi-calibrated").second shouldBe "8f5a2632c701680b41fcfe170c9cf468" - } - "integrate" { - val outputPath = tempfile("pi-", ".fits").toPath() - stacker.integrate(1, PI_01_LIGHT, PI_01_LIGHT, outputPath).shouldBeTrue() - - outputPath.openAsImage().transform(AutoScreenTransformFunction) - .save("pi-integrated").second shouldBe "bf62207dc17190009ba215da7c011297" - } - "combine LRGB" { - val outputPath = tempfile("pi-", ".fits").toPath() - stacker.combineLRGB(outputPath, PI_01_LIGHT, PI_01_LIGHT).shouldBeTrue() - - outputPath.openAsImage().transform(AutoScreenTransformFunction) - .save("pi-lrgb-combined").second shouldBe "9100d3ce892f05f4b832b2fb5f35b5a1" - } - "combine mono luminance" { - val outputPath = tempfile("pi-", ".fits").toPath() - stacker.combineLuminance(outputPath, PI_01_LIGHT, PI_01_LIGHT, true).shouldBeTrue() - - outputPath.openAsImage().transform(AutoScreenTransformFunction) - .save("pi-mono-luminance-combined").second shouldBe "85de365a9895234222acdc6e9feb7009" - } +@NonGitHubOnly +class PixInsightStackerTest : AbstractTest() { + + @Test + fun align() { + val outputPath = tempPath("pi-", ".fits") + val workingDirectory = tempDirectory("pi-") + val stacker = PixInsightStacker(RUNNER, workingDirectory) + stacker.align(PI_01_LIGHT, PI_03_LIGHT, outputPath).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-aligned").second shouldBe "106651a7c1e640852384284ec12e0977" + } + + @Test + fun calibrate() { + val outputPath = tempPath("pi-", ".fits") + val workingDirectory = tempDirectory("pi-") + val stacker = PixInsightStacker(RUNNER, workingDirectory) + stacker.calibrate(PI_01_LIGHT, outputPath, PI_DARK).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-calibrated").second shouldBe "8f5a2632c701680b41fcfe170c9cf468" + } + + @Test + fun integrate() { + val outputPath = tempPath("pi-", ".fits") + val workingDirectory = tempDirectory("pi-") + val stacker = PixInsightStacker(RUNNER, workingDirectory) + stacker.integrate(1, PI_01_LIGHT, PI_01_LIGHT, outputPath).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-integrated").second shouldBe "bf62207dc17190009ba215da7c011297" + } + + @Test + fun combineLrgb() { + val outputPath = tempPath("pi-", ".fits") + val workingDirectory = tempDirectory("pi-") + val stacker = PixInsightStacker(RUNNER, workingDirectory) + stacker.combineLRGB(outputPath, PI_01_LIGHT, PI_01_LIGHT).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-lrgb-combined").second shouldBe "9100d3ce892f05f4b832b2fb5f35b5a1" + } + + @Test + fun combineMonoLuminance() { + val outputPath = tempPath("pi-", ".fits") + val workingDirectory = tempDirectory("pi-") + val stacker = PixInsightStacker(RUNNER, workingDirectory) + stacker.combineLuminance(outputPath, PI_01_LIGHT, PI_01_LIGHT, true).shouldBeTrue() + + outputPath.openAsImage().transform(AutoScreenTransformFunction) + .save("pi-mono-luminance-combined").second shouldBe "85de365a9895234222acdc6e9feb7009" + } + + companion object { + + @JvmStatic private val RUNNER = PixInsightScriptRunner(Path.of("PixInsight")) } } diff --git a/nebulosa-pixinsight/src/test/kotlin/PixInsightStarDetectorTest.kt b/nebulosa-pixinsight/src/test/kotlin/PixInsightStarDetectorTest.kt index 75d38714e..149d650e9 100644 --- a/nebulosa-pixinsight/src/test/kotlin/PixInsightStarDetectorTest.kt +++ b/nebulosa-pixinsight/src/test/kotlin/PixInsightStarDetectorTest.kt @@ -1,19 +1,18 @@ -import io.kotest.core.annotation.EnabledIf import io.kotest.matchers.collections.shouldHaveSize import nebulosa.pixinsight.script.PixInsightScriptRunner import nebulosa.pixinsight.stardetector.PixInsightStarDetector -import nebulosa.test.AbstractFitsAndXisfTest -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.NGC3344_MONO_8_FITS +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test import java.nio.file.Path -@EnabledIf(NonGitHubOnlyCondition::class) -class PixInsightStarDetectorTest : AbstractFitsAndXisfTest() { +@NonGitHubOnly +class PixInsightStarDetectorTest { - init { - "detect stars" { - val runner = PixInsightScriptRunner(Path.of("PixInsight")) - val detectedStars = PixInsightStarDetector(runner, 0).detect(NGC3344_MONO_8_FITS) - detectedStars shouldHaveSize 15 - } + @Test + fun detectStars() { + val runner = PixInsightScriptRunner(Path.of("PixInsight")) + val detectedStars = PixInsightStarDetector(runner, 0).detect(NGC3344_MONO_8_FITS) + detectedStars shouldHaveSize 15 } } diff --git a/nebulosa-platesolver/src/test/kotlin/PlateSolutionTest.kt b/nebulosa-platesolver/src/test/kotlin/PlateSolutionTest.kt index 9ef2a59f2..f329df2e4 100644 --- a/nebulosa-platesolver/src/test/kotlin/PlateSolutionTest.kt +++ b/nebulosa-platesolver/src/test/kotlin/PlateSolutionTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe @@ -8,12 +7,26 @@ import nebulosa.math.formatSignedDMS import nebulosa.math.toArcsec import nebulosa.math.toDegrees import nebulosa.platesolver.PlateSolution +import org.junit.jupiter.api.Test -class PlateSolutionTest : StringSpec() { +class PlateSolutionTest { + + @Test + fun astrometryNet() { + val header = FitsHeader.from(ASTROMETRY_NET_FITS_HEADER) + val solution = PlateSolution.from(header).shouldNotBeNull() + + solution.rightAscension.formatHMS() shouldBe "03h19m07.7s" + solution.declination.formatSignedDMS() shouldBe "-066°30'12.2\"" + solution.orientation.toDegrees shouldBe (-136.9 plusOrMinus 1e-1) + solution.scale.toArcsec shouldBe (1.37 plusOrMinus 1e-2) + solution.radius.toDegrees shouldBe (0.476 plusOrMinus 1e-3) + } + + companion object { - init { // Don't have CDELT and CROTA - val astrometryNet = "SIMPLE = T / Standard FITS file " + + @JvmStatic private val ASTROMETRY_NET_FITS_HEADER = "SIMPLE = T / Standard FITS file " + "BITPIX = 8 / ASCII or bytes array " + "NAXIS = 0 / Minimal header " + "EXTEND = T / There may be FITS ext " + @@ -36,16 +49,5 @@ class PlateSolutionTest : StringSpec() { "IMAGEW = 2072 / Image width, in pixels. " + "IMAGEH = 1410 / Image height, in pixels. " + "END " - - "astrometry.net" { - val header = FitsHeader.from(astrometryNet) - val solution = PlateSolution.from(header).shouldNotBeNull() - - solution.rightAscension.formatHMS() shouldBe "03h19m07.7s" - solution.declination.formatSignedDMS() shouldBe "-066°30'12.2\"" - solution.orientation.toDegrees shouldBe (-136.9 plusOrMinus 1e-1) - solution.scale.toArcsec shouldBe (1.37 plusOrMinus 1e-2) - solution.radius.toDegrees shouldBe (0.476 plusOrMinus 1e-3) - } } } diff --git a/nebulosa-sbd/src/test/kotlin/SmallBodyCloseApprochServiceTest.kt b/nebulosa-sbd/src/test/kotlin/SmallBodyCloseApprochServiceTest.kt index b725a05ac..80d8a634a 100644 --- a/nebulosa-sbd/src/test/kotlin/SmallBodyCloseApprochServiceTest.kt +++ b/nebulosa-sbd/src/test/kotlin/SmallBodyCloseApprochServiceTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainAll import io.kotest.matchers.collections.shouldHaveAtLeastSize import io.kotest.matchers.collections.shouldHaveSize @@ -8,51 +7,55 @@ import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import nebulosa.constants.AU_KM import nebulosa.sbd.SmallBodyDatabaseService +import nebulosa.test.HTTP_CLIENT +import org.junit.jupiter.api.Test import java.time.LocalDate -class SmallBodyCloseApprochServiceTest : StringSpec() { - - init { - val service = SmallBodyDatabaseService() - - "search" { - val data = service.closeApproaches(distance = 10.0, date = LocalDate.of(2024, 3, 13)).execute().body() - - data.shouldNotBeNull() - data.count shouldBeGreaterThanOrEqual 1 - - data.fields shouldHaveSize 14 - data.data shouldHaveAtLeastSize 10 - - data.data[0][0] shouldBe "2024 EC3" - data.data[0][3] shouldBe "2024-Mar-13 07:22" - data.data[0][4].toDouble() shouldBe (2.29 * 384400 / AU_KM plusOrMinus 1e-2) - - data.data.map { it[0] } shouldContainAll listOf( - "2024 EC3", "2024 EL1", "2024 EH3", - "2024 EK3", "2024 EQ", "2024 EK", "2024 EX2", "2024 ED3", - "2024 EN", "2020 FD" - ) - - // https://minorplanetcenter.net/ - // ############# Close Approaches ############ - // 2024 EC3 Mar 13 07:22 2.29 4-11 - // 2024 EL1 Mar 13 13:10 8.48 9-30 - // 2024 EH3 Mar 14 16:16 6.5 16-50 - // 2024 EK3 Mar 15 03:10 1.62 3-11 - // 2020 FU Mar 15 21:29 14.84 10-31 - // 2024 CJ8 Mar 16 01:25 17.21 43-140 - // 2024 EQ Mar 16 06:31 6.92 12-37 - // 2022 EK1 Mar 16 09:08 45.25 22-69 - // 2024 EK Mar 16 15:29 6.96 8-26 - // 2024 EX2 Mar 16 16:03 5.82 10-32 - // 2004 FK2 Mar 17 23:18 24.01 24-75 - // 2024 EW Mar 18 07:55 32.45 14-43 - // 2024 ED3 Mar 18 08:37 3.5 17-54 - // 2024 CH6 Mar 18 08:42 47.08 61-190 - // 2024 EN Mar 18 18:34 3.87 23-74 - // 2020 FD Mar 18 19:11 4.52 5-17 - // 2011 BY24 Mar 18 22:50 26.14 56-180 - } +class SmallBodyCloseApprochServiceTest { + + @Test + fun search() { + val data = SERVICE.closeApproaches(distance = 10.0, date = LocalDate.of(2024, 3, 13)).execute().body() + + data.shouldNotBeNull() + data.count shouldBeGreaterThanOrEqual 1 + + data.fields shouldHaveSize 14 + data.data shouldHaveAtLeastSize 10 + + data.data[0][0] shouldBe "2024 EC3" + data.data[0][3] shouldBe "2024-Mar-13 07:22" + data.data[0][4].toDouble() shouldBe (2.29 * 384400 / AU_KM plusOrMinus 1e-2) + + data.data.map { it[0] } shouldContainAll listOf( + "2024 EC3", "2024 EL1", "2024 EH3", + "2024 EK3", "2024 EQ", "2024 EK", "2024 EX2", "2024 ED3", + "2024 EN", "2020 FD" + ) + + // https://minorplanetcenter.net/ + // ############# Close Approaches ############ + // 2024 EC3 Mar 13 07:22 2.29 4-11 + // 2024 EL1 Mar 13 13:10 8.48 9-30 + // 2024 EH3 Mar 14 16:16 6.5 16-50 + // 2024 EK3 Mar 15 03:10 1.62 3-11 + // 2020 FU Mar 15 21:29 14.84 10-31 + // 2024 CJ8 Mar 16 01:25 17.21 43-140 + // 2024 EQ Mar 16 06:31 6.92 12-37 + // 2022 EK1 Mar 16 09:08 45.25 22-69 + // 2024 EK Mar 16 15:29 6.96 8-26 + // 2024 EX2 Mar 16 16:03 5.82 10-32 + // 2004 FK2 Mar 17 23:18 24.01 24-75 + // 2024 EW Mar 18 07:55 32.45 14-43 + // 2024 ED3 Mar 18 08:37 3.5 17-54 + // 2024 CH6 Mar 18 08:42 47.08 61-190 + // 2024 EN Mar 18 18:34 3.87 23-74 + // 2020 FD Mar 18 19:11 4.52 5-17 + // 2011 BY24 Mar 18 22:50 26.14 56-180 + } + + companion object { + + @JvmStatic private val SERVICE = SmallBodyDatabaseService(httpClient = HTTP_CLIENT) } } diff --git a/nebulosa-sbd/src/test/kotlin/SmallBodyDatabaseLookupServiceTest.kt b/nebulosa-sbd/src/test/kotlin/SmallBodyDatabaseLookupServiceTest.kt index a01795101..0cface85f 100644 --- a/nebulosa-sbd/src/test/kotlin/SmallBodyDatabaseLookupServiceTest.kt +++ b/nebulosa-sbd/src/test/kotlin/SmallBodyDatabaseLookupServiceTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.ints.shouldBeGreaterThanOrEqual @@ -7,41 +5,49 @@ import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import nebulosa.sbd.SmallBodyDatabaseService -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.HTTP_CLIENT +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class SmallBodyDatabaseLookupServiceTest : StringSpec() { +class SmallBodyDatabaseLookupServiceTest { - init { - val service = SmallBodyDatabaseService() + @Test + fun searchMatchesSingleRecord() { + val body = SERVICE.search("C/2017 K2").execute().body().shouldNotBeNull() + body.list.shouldBeNull() + body.orbit!!.equinox shouldBe "J2000" + body.body!!.fullname shouldBe "C/2017 K2 (PANSTARRS)" + body.body!!.spkId shouldBeExactly 1003517 + } + + @Test + fun searchNeo() { + val body = SERVICE.search("2023 GA2").execute().body().shouldNotBeNull() + body.list.shouldBeNull() + body.orbit!!.equinox shouldBe "J2000" + body.body!!.fullname shouldBe "(2023 GA2)" + body.body!!.spkId shouldBeExactly 54354395 + } + + @Test + fun searchMatchesMultipleRecords() { + val body = SERVICE.search("PANSTARRS").execute().body().shouldNotBeNull() + val list = body.list.shouldNotBeNull() + list.size shouldBeGreaterThanOrEqual 257 + list.any { it.name == "253P/PANSTARRS" }.shouldBeTrue() + } + + @Test + fun searchFailed() { + val body = SERVICE.search("ggdgdfgdfgdg").execute().body().shouldNotBeNull() + body.list.shouldBeNull() + body.orbit.shouldBeNull() + body.body.shouldBeNull() + body.physical.shouldBeNull() + body.message shouldBe "specified object was not found" + } + + companion object { - "search matches single record" { - val body = service.search("C/2017 K2").execute().body().shouldNotBeNull() - body.list.shouldBeNull() - body.orbit!!.equinox shouldBe "J2000" - body.body!!.fullname shouldBe "C/2017 K2 (PANSTARRS)" - body.body!!.spkId shouldBeExactly 1003517 - } - "search NEO" { - val body = service.search("2023 GA2").execute().body().shouldNotBeNull() - body.list.shouldBeNull() - body.orbit!!.equinox shouldBe "J2000" - body.body!!.fullname shouldBe "(2023 GA2)" - body.body!!.spkId shouldBeExactly 54354395 - } - "search matches multiple records" { - val body = service.search("PANSTARRS").execute().body().shouldNotBeNull() - val list = body.list.shouldNotBeNull() - list.size shouldBeGreaterThanOrEqual 257 - list.any { it.name == "253P/PANSTARRS" }.shouldBeTrue() - } - "search failed" { - val body = service.search("ggdgdfgdfgdg").execute().body().shouldNotBeNull() - body.list.shouldBeNull() - body.orbit.shouldBeNull() - body.body.shouldBeNull() - body.physical.shouldBeNull() - body.message shouldBe "specified object was not found" - } + @JvmStatic private val SERVICE = SmallBodyDatabaseService(httpClient = HTTP_CLIENT) } } diff --git a/nebulosa-sbd/src/test/kotlin/SmallBodyIdentificationServiceTest.kt b/nebulosa-sbd/src/test/kotlin/SmallBodyIdentificationServiceTest.kt index 2b65c35db..cc3b9cb36 100644 --- a/nebulosa-sbd/src/test/kotlin/SmallBodyIdentificationServiceTest.kt +++ b/nebulosa-sbd/src/test/kotlin/SmallBodyIdentificationServiceTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.ints.shouldBeExactly @@ -8,36 +7,42 @@ import nebulosa.math.deg import nebulosa.math.hours import nebulosa.math.km import nebulosa.sbd.SmallBodyDatabaseService +import nebulosa.test.HTTP_CLIENT +import org.junit.jupiter.api.Test import java.time.LocalDateTime -class SmallBodyIdentificationServiceTest : StringSpec() { +class SmallBodyIdentificationServiceTest { - init { - val service = SmallBodyDatabaseService() + @Test + fun searchAroundCeres() { + val data = SERVICE.identify( + LocalDateTime.of(2023, 8, 21, 0, 0, 0, 0), + // Observatorio do Pico dos Dias, Itajuba (observatory) [code: 874] + (-22.5354318).deg, (-45.5827).deg, 1.81754.km, + "13 21 16.50".hours, "-01 57 06.5".deg, 1.0.deg, + ).execute().body() - "search around Ceres" { - val data = service.identify( - LocalDateTime.of(2023, 8, 21, 0, 0, 0, 0), - // Observatorio do Pico dos Dias, Itajuba (observatory) [code: 874] - (-22.5354318).deg, (-45.5827).deg, 1.81754.km, - "13 21 16.50".hours, "-01 57 06.5".deg, 1.0.deg, - ).execute().body() + data.shouldNotBeNull() + data.count shouldBeGreaterThanOrEqual 1 + data.data.any { "Ceres" in it[0] }.shouldBeTrue() + } + + @Test + fun noMatchingRecords() { + val data = SERVICE.identify( + LocalDateTime.of(2023, 1, 15, 1, 38, 15, 0), + // Observatorio do Pico dos Dias, Itajuba (observatory) [code: 874] + (-22.5354318).deg, (-45.5827).deg, 1.81754.km, + "10 44 02".hours, "-59 36 04".deg, 1.0.deg, + ).execute().body() + + data.shouldNotBeNull() + data.count shouldBeExactly 0 + data.data.shouldBeEmpty() + } - data.shouldNotBeNull() - data.count shouldBeGreaterThanOrEqual 1 - data.data.any { "Ceres" in it[0] }.shouldBeTrue() - } - "no matching records" { - val data = service.identify( - LocalDateTime.of(2023, 1, 15, 1, 38, 15, 0), - // Observatorio do Pico dos Dias, Itajuba (observatory) [code: 874] - (-22.5354318).deg, (-45.5827).deg, 1.81754.km, - "10 44 02".hours, "-59 36 04".deg, 1.0.deg, - ).execute().body() + companion object { - data.shouldNotBeNull() - data.count shouldBeExactly 0 - data.data.shouldBeEmpty() - } + @JvmStatic private val SERVICE = SmallBodyDatabaseService(httpClient = HTTP_CLIENT) } } diff --git a/nebulosa-simbad/src/test/kotlin/SimbadServiceTest.kt b/nebulosa-simbad/src/test/kotlin/SimbadServiceTest.kt index 23bde8e13..e913824cf 100644 --- a/nebulosa-simbad/src/test/kotlin/SimbadServiceTest.kt +++ b/nebulosa-simbad/src/test/kotlin/SimbadServiceTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveAtLeastSize import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe @@ -9,120 +7,120 @@ import nebulosa.math.arcmin import nebulosa.math.deg import nebulosa.math.hours import nebulosa.simbad.SimbadService -import nebulosa.test.NonGitHubOnlyCondition -import okhttp3.OkHttpClient -import okhttp3.logging.HttpLoggingInterceptor -import java.util.concurrent.TimeUnit - -@EnabledIf(NonGitHubOnlyCondition::class) -class SimbadServiceTest : StringSpec() { - - init { - val httpClient = OkHttpClient.Builder() - .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) - .callTimeout(1, TimeUnit.MINUTES) - .connectTimeout(1, TimeUnit.MINUTES) - .readTimeout(1, TimeUnit.MINUTES) - .build() - - val service = SimbadService(httpClient = httpClient) - val basicTable = From("basic").alias("b") - val identTable = From("ident").alias("i") - val otypesTable = From("otypes").alias("o") - val fluxTable = From("allfluxes").alias("f") - - val oidColumn = basicTable.column("oid") - val mainIdColumn = basicTable.column("main_id") - val otypeColumn = basicTable.column("otype") - val raColumn = basicTable.column("ra") - val decColumn = basicTable.column("dec") - val pmRAColumn = basicTable.column("pmra") - val pmDECColumn = basicTable.column("pmdec") - val plxColumn = basicTable.column("plx_value") - val radVelColumn = basicTable.column("rvz_radvel") - val redshiftColumn = basicTable.column("rvz_redshift") - val nameColumn = identTable.column("id") - val magColumn = fluxTable.column("V") - - "identifier query" { - val query = QueryBuilder() - query.addAll(listOf(oidColumn, otypeColumn, raColumn, decColumn, pmRAColumn, pmDECColumn)) - query.addAll(listOf(plxColumn, radVelColumn, redshiftColumn, mainIdColumn)) - val join = InnerJoin(basicTable, identTable, arrayOf(oidColumn equal Column("i.oidref"))) - query.add(join) - query.add(nameColumn equal "ngc5128") - - val call = service.query(query.build()) - val rows = call.execute().body().shouldNotBeNull() - - with(rows[0]) { - getField("oid") shouldBe "3392496" - getField("ra") shouldStartWith "201.3650" - getField("dec") shouldStartWith "-43.0191" - } +import nebulosa.test.HTTP_CLIENT +import nebulosa.test.NonGitHubOnly +import org.junit.jupiter.api.Test + +@NonGitHubOnly +class SimbadServiceTest { + + @Test + fun identifierQuery() { + val query = QueryBuilder() + query.addAll(listOf(OID_COLUMN, OTYPE_COLUMN, RA_COLUMN, DEC_COLUMN, PM_RA_COLUMN, PM_DEC_COLUMN)) + query.addAll(listOf(PLX_COLUMN, RAD_VAL_COLUMN, REDSHIFT_COLUMN, MAIN_ID_COLUMN)) + val join = InnerJoin(BASIC_TABLE, IDENT_TABLE, arrayOf(OID_COLUMN equal Column("i.oidref"))) + query.add(join) + query.add(NAME_COLUMN equal "ngc5128") + + val call = SERVICE.query(query.build()) + val rows = call.execute().body().shouldNotBeNull() + + with(rows[0]) { + getField("oid") shouldBe "3392496" + getField("ra") shouldStartWith "201.3650" + getField("dec") shouldStartWith "-43.0191" } - "coordinate query" { - val query = QueryBuilder() - query.addAll(listOf(oidColumn, otypeColumn, raColumn, decColumn, pmRAColumn, pmDECColumn)) - query.addAll(listOf(plxColumn, radVelColumn, redshiftColumn, mainIdColumn)) - query.add(basicTable) - query.add(SkyPoint(raColumn, decColumn) contains Circle("20 54 05.689".hours, "+37 01 17.38".deg, 2.0.arcmin)) - - val call = service.query(query.build()) - val rows = call.execute().body().shouldNotBeNull() - - rows shouldHaveAtLeastSize 4 - - with(rows.first { it.getField("main_id") == "TYC 2700-2084-1" }) { - getField("oid") shouldBe "6742437" - getField("ra") shouldStartWith "313.5013" - getField("dec") shouldStartWith "37.0192" - } + } + + @Test + fun coordinateQuery() { + val query = QueryBuilder() + query.addAll(listOf(OID_COLUMN, OTYPE_COLUMN, RA_COLUMN, DEC_COLUMN, PM_RA_COLUMN, PM_DEC_COLUMN)) + query.addAll(listOf(PLX_COLUMN, RAD_VAL_COLUMN, REDSHIFT_COLUMN, MAIN_ID_COLUMN)) + query.add(BASIC_TABLE) + query.add(SkyPoint(RA_COLUMN, DEC_COLUMN) contains Circle("20 54 05.689".hours, "+37 01 17.38".deg, 2.0.arcmin)) + + val call = SERVICE.query(query.build()) + val rows = call.execute().body().shouldNotBeNull() + + rows shouldHaveAtLeastSize 4 + + with(rows.first { it.getField("main_id") == "TYC 2700-2084-1" }) { + getField("oid") shouldBe "6742437" + getField("ra") shouldStartWith "313.5013" + getField("dec") shouldStartWith "37.0192" } - "object type query" { - val query = QueryBuilder() - query.add(Limit(100)) - query.addAll(listOf(oidColumn, otypeColumn, raColumn, decColumn, pmRAColumn, pmDECColumn)) - query.addAll(listOf(plxColumn, radVelColumn, redshiftColumn, mainIdColumn)) - val join = InnerJoin(basicTable, otypesTable, arrayOf(oidColumn equal otypesTable.column("oidref"))) - query.add(join) - query.add(otypesTable.column("otype") equal "*") - - val call = service.query(query.build()) - val rows = call.execute().body().shouldNotBeNull() - - rows shouldHaveAtLeastSize 100 - - with(rows.first { it.getField("main_id") == "TYC 2713-2426-1" }) { - getField("oid") shouldBe "86" - getField("ra") shouldStartWith "316.7063" - getField("dec") shouldStartWith "36.7207" - getField("otype") shouldBe "PM*" - } + } + + @Test + fun objectTypeQuery() { + val query = QueryBuilder() + query.add(Limit(100)) + query.addAll(listOf(OID_COLUMN, OTYPE_COLUMN, RA_COLUMN, DEC_COLUMN, PM_RA_COLUMN, PM_DEC_COLUMN)) + query.addAll(listOf(PLX_COLUMN, RAD_VAL_COLUMN, REDSHIFT_COLUMN, MAIN_ID_COLUMN)) + val join = InnerJoin(BASIC_TABLE, OTYPES_TABLE, arrayOf(OID_COLUMN equal OTYPES_TABLE.column("oidref"))) + query.add(join) + query.add(OTYPES_TABLE.column("otype") equal "*") + + val call = SERVICE.query(query.build()) + val rows = call.execute().body().shouldNotBeNull() + + rows shouldHaveAtLeastSize 100 + + with(rows.first { it.getField("main_id") == "TYC 2713-2426-1" }) { + getField("oid") shouldBe "86" + getField("ra") shouldStartWith "316.7063" + getField("dec") shouldStartWith "36.7207" + getField("otype") shouldBe "PM*" } - "constellation query" { - val query = QueryBuilder() - query.add(Limit(100)) - query.addAll(listOf(oidColumn, otypeColumn, raColumn, decColumn, pmRAColumn, pmDECColumn)) - query.addAll(listOf(plxColumn, radVelColumn, redshiftColumn, mainIdColumn, magColumn)) - val join = InnerJoin(basicTable, fluxTable, arrayOf(oidColumn equal fluxTable.column("oidref"))) - query.add(join) - query.add(raColumn.isNotNull) - query.add(SkyPoint(raColumn, decColumn) contains ConstellationBoundary("CRU")) - query.add(SortBy(magColumn)) - - val call = service.query(query.build()) - val rows = call.execute().body().shouldNotBeNull() - - rows shouldHaveAtLeastSize 100 - - with(rows.first { it.getField("main_id") == "Cl Collinder 258" }) { - getField("oid") shouldBe "3297061" - getField("otype") shouldBe "OpC" - getField("ra") shouldStartWith "186.798" - getField("dec") shouldStartWith "-60.767" - getField("V") shouldBe "7.1" - } + } + + @Test + fun constellationQuery() { + val query = QueryBuilder() + query.add(Limit(100)) + query.addAll(listOf(OID_COLUMN, OTYPE_COLUMN, RA_COLUMN, DEC_COLUMN, PM_RA_COLUMN, PM_DEC_COLUMN)) + query.addAll(listOf(PLX_COLUMN, RAD_VAL_COLUMN, REDSHIFT_COLUMN, MAIN_ID_COLUMN, MAG_COLUMN)) + val join = InnerJoin(BASIC_TABLE, FLUX_TABLE, arrayOf(OID_COLUMN equal FLUX_TABLE.column("oidref"))) + query.add(join) + query.add(RA_COLUMN.isNotNull) + query.add(SkyPoint(RA_COLUMN, DEC_COLUMN) contains ConstellationBoundary("CRU")) + query.add(SortBy(MAG_COLUMN)) + + val call = SERVICE.query(query.build()) + val rows = call.execute().body().shouldNotBeNull() + + rows shouldHaveAtLeastSize 100 + + with(rows.first { it.getField("main_id") == "Cl Collinder 258" }) { + getField("oid") shouldBe "3297061" + getField("otype") shouldBe "OpC" + getField("ra") shouldStartWith "186.798" + getField("dec") shouldStartWith "-60.767" + getField("V") shouldBe "7.1" } } + + companion object { + + @JvmStatic private val SERVICE = SimbadService(httpClient = HTTP_CLIENT) + @JvmStatic private val BASIC_TABLE = From("basic").alias("b") + @JvmStatic private val IDENT_TABLE = From("ident").alias("i") + @JvmStatic private val OTYPES_TABLE = From("otypes").alias("o") + @JvmStatic private val FLUX_TABLE = From("allfluxes").alias("f") + + @JvmStatic private val OID_COLUMN = BASIC_TABLE.column("oid") + @JvmStatic private val MAIN_ID_COLUMN = BASIC_TABLE.column("main_id") + @JvmStatic private val OTYPE_COLUMN = BASIC_TABLE.column("otype") + @JvmStatic private val RA_COLUMN = BASIC_TABLE.column("ra") + @JvmStatic private val DEC_COLUMN = BASIC_TABLE.column("dec") + @JvmStatic private val PM_RA_COLUMN = BASIC_TABLE.column("pmra") + @JvmStatic private val PM_DEC_COLUMN = BASIC_TABLE.column("pmdec") + @JvmStatic private val PLX_COLUMN = BASIC_TABLE.column("plx_value") + @JvmStatic private val RAD_VAL_COLUMN = BASIC_TABLE.column("rvz_radvel") + @JvmStatic private val REDSHIFT_COLUMN = BASIC_TABLE.column("rvz_redshift") + @JvmStatic private val NAME_COLUMN = IDENT_TABLE.column("id") + @JvmStatic private val MAG_COLUMN = FLUX_TABLE.column("V") + } } diff --git a/nebulosa-siril/src/test/kotlin/SirilTest.kt b/nebulosa-siril/src/test/kotlin/SirilTest.kt index b28545f92..bff8557e2 100644 --- a/nebulosa-siril/src/test/kotlin/SirilTest.kt +++ b/nebulosa-siril/src/test/kotlin/SirilTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.engine.spec.tempdir import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.doubles.plusOrMinus @@ -11,72 +9,78 @@ import nebulosa.platesolver.Parity import nebulosa.siril.livestacker.SirilLiveStacker import nebulosa.siril.platesolver.SirilPlateSolver import nebulosa.siril.stardetector.SirilStarDetector -import nebulosa.test.AbstractFitsAndXisfTest -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.* +import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.io.path.copyTo import kotlin.io.path.listDirectoryEntries -@EnabledIf(NonGitHubOnlyCondition::class) -class SirilTest : AbstractFitsAndXisfTest() { +@NonGitHubOnly +class SirilTest : AbstractTest() { - init { - val executablePath = Path.of("siril-cli") + @Test + fun liveStacking() { + val workingDirectory = dataDirectory.concat("/siril") - "live stacking" { - val workingDirectory = Path.of("../data/siril").toRealPath() + SirilLiveStacker(EXECUTABLE_PATH, workingDirectory).use { + it.start() - SirilLiveStacker(executablePath, workingDirectory).use { - it.start() + val inputDir = tempDirectory("ls-") - val fitsDir = tempdir().toPath() + PI_01_LIGHT.copyTo(inputDir.concat("01.fits")) + PI_02_LIGHT.copyTo(inputDir.concat("02.fits")) + PI_03_LIGHT.copyTo(inputDir.concat("03.fits")) + PI_04_LIGHT.copyTo(inputDir.concat("04.fits")) - PI_01_LIGHT.copyTo(Path.of("$fitsDir", "01.fits")) - PI_02_LIGHT.copyTo(Path.of("$fitsDir", "02.fits")) - PI_03_LIGHT.copyTo(Path.of("$fitsDir", "03.fits")) - PI_04_LIGHT.copyTo(Path.of("$fitsDir", "04.fits")) + for (fits in inputDir.listDirectoryEntries().shouldHaveSize(4).sorted()) { + it.add(fits).shouldNotBeNull() + } - for (fits in fitsDir.listDirectoryEntries().shouldHaveSize(4).sorted()) { - it.add(fits).shouldNotBeNull() - } + workingDirectory.listDirectoryEntries().shouldHaveSize(5) + } - workingDirectory.listDirectoryEntries().shouldHaveSize(5) - } + workingDirectory.listDirectoryEntries().shouldHaveSize(1) + } + + @Test + fun plateSolver() { + val solver = SirilPlateSolver(EXECUTABLE_PATH) + val solution = solver.solve(PI_01_LIGHT, null) + + solution.solved.shouldBeTrue() + solution.orientation.toDegrees shouldBe (-90.02 plusOrMinus 1e-2) + solution.rightAscension.formatHMS() shouldBe "00h06m46.0s" + solution.declination.formatSignedDMS() shouldBe "+089°51'42.0\"" + solution.scale.toArcsec shouldBe (3.575 plusOrMinus 1e-3) + solution.width.formatDMS() shouldBe "001°16'16.3\"" + solution.height.formatDMS() shouldBe "001°01'01.1\"" + solution.parity shouldBe Parity.FLIPPED + solution.widthInPixels shouldBeExactly 1280.0 + solution.heightInPixels shouldBeExactly 1024.0 + } + + @Test + fun starDetector() { + val detector = SirilStarDetector(EXECUTABLE_PATH) - workingDirectory.listDirectoryEntries().shouldHaveSize(1) + with(detector.detect(PI_FOCUS_0)) { + this shouldHaveSize 307 + map { it.hfd }.average() shouldBe (7.9 plusOrMinus 1e-1) } - "plate solver" { - val solver = SirilPlateSolver(executablePath) - val solution = solver.solve(PI_01_LIGHT, null) - - solution.solved.shouldBeTrue() - solution.orientation.toDegrees shouldBe (-90.02 plusOrMinus 1e-2) - solution.rightAscension.formatHMS() shouldBe "00h06m46.0s" - solution.declination.formatSignedDMS() shouldBe "+089°51'42.0\"" - solution.scale.toArcsec shouldBe (3.575 plusOrMinus 1e-3) - solution.width.formatDMS() shouldBe "001°16'16.3\"" - solution.height.formatDMS() shouldBe "001°01'01.1\"" - solution.parity shouldBe Parity.FLIPPED - solution.widthInPixels shouldBeExactly 1280.0 - solution.heightInPixels shouldBeExactly 1024.0 + + with(detector.detect(PI_FOCUS_30000)) { + this shouldHaveSize 258 + map { it.hfd }.average() shouldBe (1.1 plusOrMinus 1e-1) } - "star detector" { - val detector = SirilStarDetector(executablePath) - with(detector.detect(PI_FOCUS_0)) { - this shouldHaveSize 307 - map { it.hfd }.average() shouldBe (7.9 plusOrMinus 1e-1) - } + with(detector.detect(PI_FOCUS_100000)) { + this shouldHaveSize 82 + map { it.hfd }.average() shouldBe (22.4 plusOrMinus 1e-1) + } + } - with(detector.detect(PI_FOCUS_30000)) { - this shouldHaveSize 258 - map { it.hfd }.average() shouldBe (1.1 plusOrMinus 1e-1) - } + companion object { - with(detector.detect(PI_FOCUS_100000)) { - this shouldHaveSize 82 - map { it.hfd }.average() shouldBe (22.4 plusOrMinus 1e-1) - } - } + @JvmStatic private val EXECUTABLE_PATH = Path.of("siril-cli") } } diff --git a/nebulosa-skycatalog-hyg/src/test/kotlin/HygDatabaseTest.kt b/nebulosa-skycatalog-hyg/src/test/kotlin/HygDatabaseTest.kt index a0e405c83..18b1c0e13 100644 --- a/nebulosa-skycatalog-hyg/src/test/kotlin/HygDatabaseTest.kt +++ b/nebulosa-skycatalog-hyg/src/test/kotlin/HygDatabaseTest.kt @@ -1,20 +1,21 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe -import nebulosa.io.resource import nebulosa.skycatalog.hyg.HygDatabase -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.download +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import kotlin.io.path.inputStream -@EnabledIf(NonGitHubOnlyCondition::class) -class HygDatabaseTest : StringSpec() { +@Disabled +class HygDatabaseTest { - init { - "load" { - val database = HygDatabase() - resource("hyg_v35.csv")!!.use(database::load) - database.size shouldBe 118002 - database.withText("Alp Psc") shouldHaveSize 1 - } + @Test + fun load() { + val database = HygDatabase() + // Typo at constellation name 119628 "Eco" + val hygCsv = download("https://github.com/astronexus/HYG-Database/raw/main/hyg/CURRENT/hygdata_v41.csv") + hygCsv.inputStream().use(database::load) + database.size shouldBe 118002 + database.withText("Alp Psc") shouldHaveSize 1 } } diff --git a/nebulosa-skycatalog-hyg/src/test/resources/.gitignore b/nebulosa-skycatalog-hyg/src/test/resources/.gitignore deleted file mode 100644 index afed0735d..000000000 --- a/nebulosa-skycatalog-hyg/src/test/resources/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.csv diff --git a/nebulosa-skycatalog-sao/src/test/kotlin/SaoCatalogTest.kt b/nebulosa-skycatalog-sao/src/test/kotlin/SaoCatalogTest.kt index 6dab8d788..8b5516d2d 100644 --- a/nebulosa-skycatalog-sao/src/test/kotlin/SaoCatalogTest.kt +++ b/nebulosa-skycatalog-sao/src/test/kotlin/SaoCatalogTest.kt @@ -1,34 +1,34 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe -import nebulosa.io.bufferedResource import nebulosa.math.deg import nebulosa.math.hours import nebulosa.skycatalog.sao.SaoCatalog -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.concat +import nebulosa.test.dataDirectory +import okio.source +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class SaoCatalogTest : StringSpec() { +@Disabled +class SaoCatalogTest { - init { - "load" { - val catalog = SaoCatalog() - catalog.load(bufferedResource("SAO.pc")!!) - catalog shouldHaveSize 258997 + @Test + fun load() { + val catalog = SaoCatalog() + dataDirectory.concat("SAO.pc").source().use(catalog::load) + catalog shouldHaveSize 258997 - catalog.first().id shouldBe 1 - catalog.first().rightAscensionJ2000 shouldBe ("0 00 05.097".hours plusOrMinus 1e-14) - catalog.first().declinationJ2000 shouldBe ("82 41 41.82".deg plusOrMinus 1e-14) - catalog.first().magnitude shouldBe 7.2 - catalog.first().spType shouldBe "A0" + catalog.first().id shouldBe 1 + catalog.first().rightAscensionJ2000 shouldBe ("0 00 05.097".hours plusOrMinus 1e-14) + catalog.first().declinationJ2000 shouldBe ("82 41 41.82".deg plusOrMinus 1e-14) + catalog.first().magnitude shouldBe 7.2 + catalog.first().spType shouldBe "A0" - catalog.last().id shouldBe 258997 - catalog.last().rightAscensionJ2000 shouldBe ("23 58 52.487".hours plusOrMinus 1e-14) - catalog.last().declinationJ2000 shouldBe ("-83 48 05.02".deg plusOrMinus 1e-14) - catalog.last().magnitude shouldBe 8.9 - catalog.last().spType shouldBe "K0" - } + catalog.last().id shouldBe 258997 + catalog.last().rightAscensionJ2000 shouldBe ("23 58 52.487".hours plusOrMinus 1e-14) + catalog.last().declinationJ2000 shouldBe ("-83 48 05.02".deg plusOrMinus 1e-14) + catalog.last().magnitude shouldBe 8.9 + catalog.last().spType shouldBe "K0" } } diff --git a/nebulosa-skycatalog-sao/src/test/resources/.gitignore b/nebulosa-skycatalog-sao/src/test/resources/.gitignore deleted file mode 100644 index 6fd0ef029..000000000 --- a/nebulosa-skycatalog-sao/src/test/resources/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.pc diff --git a/nebulosa-skycatalog-stellarium/src/test/kotlin/NebulaTest.kt b/nebulosa-skycatalog-stellarium/src/test/kotlin/NebulaTest.kt index ad244ed64..5fec73507 100644 --- a/nebulosa-skycatalog-stellarium/src/test/kotlin/NebulaTest.kt +++ b/nebulosa-skycatalog-stellarium/src/test/kotlin/NebulaTest.kt @@ -1,43 +1,46 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldContainAll import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.ints.shouldBeExactly import nebulosa.math.deg import nebulosa.math.hours import nebulosa.skycatalog.stellarium.Nebula -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.AbstractTest +import nebulosa.test.download import okio.gzip import okio.source -import java.nio.file.Path - -@EnabledIf(NonGitHubOnlyCondition::class) -class NebulaTest : StringSpec() { - - init { - "load" { - val catalog = Path.of("../data/catalog.dat").source().gzip() - val names = Path.of("../data/names.dat").source() - - val nebula = Nebula() - nebula.load(catalog, names) - - nebula.size shouldBeExactly 94661 - - nebula - .searchAround("05 35 16.8".hours, "-05 23 24".deg, 1.0.deg) - .onEach { println(it) } - .size shouldBeExactly 11 - - nebula - .searchAround("18 02 42.0".hours, "-22 58 18".deg, 1.0.deg) - .onEach { println(it) } - .size shouldBeExactly 19 - } - "names" { - val names = Path.of("../data/names.dat").source().use(Nebula::namesFor) - val thorHelmet = names.filter { it.id == "NGC 2359" } shouldHaveSize 5 - thorHelmet.map { it.name }.shouldContainAll("Thor's Helmet", "Duck Head Nebula", "Flying Eye Nebula", "Duck Nebula", "Whistle Nebula") - } +import org.junit.jupiter.api.Test + +class NebulaTest : AbstractTest() { + + @Test + fun load() { + val catalogPath = download("https://github.com/Stellarium/stellarium/raw/master/nebulae/default/catalog.dat") + val catalog = catalogPath.source().gzip().autoClose() + + val namesPath = download("https://github.com/Stellarium/stellarium/raw/master/nebulae/default/names.dat") + val names = namesPath.source().autoClose() + + val nebula = Nebula() + nebula.load(catalog, names) + + nebula.size shouldBeExactly 94660 + + nebula + .searchAround("05 35 16.8".hours, "-05 23 24".deg, 1.0.deg) + .onEach { println(it) } + .size shouldBeExactly 11 + + nebula + .searchAround("18 02 42.0".hours, "-22 58 18".deg, 1.0.deg) + .onEach { println(it) } + .size shouldBeExactly 19 + } + + @Test + fun names() { + val namesPath = download("https://github.com/Stellarium/stellarium/raw/master/nebulae/default/names.dat") + val names = namesPath.source().use(Nebula::namesFor) + val thorHelmet = names.filter { it.id == "NGC 2359" } shouldHaveSize 5 + thorHelmet.map { it.name }.shouldContainAll("Thor's Helmet", "Duck Head Nebula", "Flying Eye Nebula", "Duck Nebula", "Whistle Nebula") } } diff --git a/nebulosa-skycatalog/src/test/kotlin/GeodesicGridTest.kt b/nebulosa-skycatalog/src/test/kotlin/GeodesicGridTest.kt index 99b2a0565..39bc7404b 100644 --- a/nebulosa-skycatalog/src/test/kotlin/GeodesicGridTest.kt +++ b/nebulosa-skycatalog/src/test/kotlin/GeodesicGridTest.kt @@ -1,16 +1,15 @@ -import io.kotest.core.annotation.Ignored -import io.kotest.core.spec.style.StringSpec import nebulosa.math.Vector3D import nebulosa.skycatalog.GeodesicGrid +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test -@Ignored -class GeodesicGridTest : StringSpec(), GeodesicGrid.Traverser { +@Disabled +class GeodesicGridTest : GeodesicGrid.Traverser { - init { - "visit" { - val grid = GeodesicGrid(2) - grid.visitTriangles(2, this@GeodesicGridTest) - } + @Test + fun visit() { + val grid = GeodesicGrid(2) + grid.visitTriangles(2, this@GeodesicGridTest) } override fun traverse(level: Int, index: Int, c0: Vector3D, c1: Vector3D, c2: Vector3D) { diff --git a/nebulosa-test/build.gradle.kts b/nebulosa-test/build.gradle.kts index 6ab8f1936..c866db4e8 100644 --- a/nebulosa-test/build.gradle.kts +++ b/nebulosa-test/build.gradle.kts @@ -9,7 +9,9 @@ dependencies { api(project(":nebulosa-fits")) api(project(":nebulosa-xisf")) api(libs.okhttp) - api(libs.bundles.kotest) + api(libs.kotest) + api(libs.junit.api) + runtimeOnly(libs.junit.engine) } publishing { diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/AbstractFitsAndXisfTest.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/AbstractFitsAndXisfTest.kt deleted file mode 100644 index 2ef1bcedc..000000000 --- a/nebulosa-test/src/main/kotlin/nebulosa/test/AbstractFitsAndXisfTest.kt +++ /dev/null @@ -1,253 +0,0 @@ -package nebulosa.test - -import io.kotest.core.listeners.TestListener -import io.kotest.core.spec.style.StringSpec -import io.kotest.core.test.TestCase -import io.kotest.core.test.TestResult -import io.kotest.core.test.TestScope -import nebulosa.hips2fits.Hips2FitsService -import nebulosa.hips2fits.HipsSurvey -import nebulosa.image.format.ImageHdu -import nebulosa.io.transferAndCloseOutput -import nebulosa.math.Angle -import nebulosa.math.deg -import nebulosa.math.hours -import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.logging.HttpLoggingInterceptor -import okio.ByteString.Companion.toByteString -import java.awt.image.BufferedImage -import java.awt.image.DataBufferByte -import java.awt.image.DataBufferInt -import java.io.Closeable -import java.nio.file.Path -import java.util.concurrent.ConcurrentHashMap -import java.util.concurrent.ConcurrentLinkedDeque -import java.util.concurrent.TimeUnit -import javax.imageio.ImageIO -import kotlin.io.path.* - -@Suppress("PropertyName") -abstract class AbstractFitsAndXisfTest : StringSpec() { - - protected val M82_MONO_8_LZ4_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.LZ4.xisf") } - protected val M82_MONO_8_LZ4_HC_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.LZ4-HC.xisf") } - protected val M82_MONO_8_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.xisf") } - protected val M82_MONO_8_ZLIB_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.ZLib.xisf") } - protected val M82_MONO_8_ZSTANDARD_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.ZStandard.xisf") } - protected val M82_MONO_16_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.16.xisf") } - protected val M82_MONO_32_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.32.xisf") } - protected val M82_MONO_F32_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.F32.xisf") } - protected val M82_MONO_F64_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.F64.xisf") } - - protected val M82_COLOR_8_LZ4_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.LZ4.xisf") } - protected val M82_COLOR_8_LZ4_HC_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.LZ4-HC.xisf") } - protected val M82_COLOR_8_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.xisf") } - protected val M82_COLOR_8_ZLIB_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.ZLib.xisf") } - protected val M82_COLOR_8_ZSTANDARD_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.ZStandard.xisf") } - protected val M82_COLOR_16_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.16.xisf") } - protected val M82_COLOR_32_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.32.xisf") } - protected val M82_COLOR_F32_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.F32.xisf") } - protected val M82_COLOR_F64_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.F64.xisf") } - protected val DEBAYER_XISF_PATH by lazy { download("$GITHUB_XISF_URL/Debayer.xisf") } - - protected val NGC3344_MONO_8_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.8.fits") } - protected val NGC3344_MONO_16_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.16.fits") } - protected val NGC3344_MONO_32_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.32.fits") } - protected val NGC3344_MONO_F32_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.F32.fits") } - protected val NGC3344_MONO_F64_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.F64.fits") } - - protected val NGC3344_COLOR_8_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.8.fits") } - protected val NGC3344_COLOR_16_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.16.fits") } - protected val NGC3344_COLOR_32_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.32.fits") } - protected val NGC3344_COLOR_F32_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.F32.fits") } - protected val NGC3344_COLOR_F64_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.F64.fits") } - - protected val PALETTE_MONO_8_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.8.fits") } - protected val PALETTE_MONO_16_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.16.fits") } - protected val PALETTE_MONO_32_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.32.fits") } - protected val PALETTE_MONO_F32_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.F32.fits") } - protected val PALETTE_MONO_F64_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.F64.fits") } - - protected val PALETTE_COLOR_8_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.8.fits") } - protected val PALETTE_COLOR_16_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.16.fits") } - protected val PALETTE_COLOR_32_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.32.fits") } - protected val PALETTE_COLOR_F32_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.F32.fits") } - protected val PALETTE_COLOR_F64_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.F64.fits") } - - protected val PALETTE_MONO_8_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.8.xisf") } - protected val PALETTE_MONO_16_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.16.xisf") } - protected val PALETTE_MONO_32_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.32.xisf") } - protected val PALETTE_MONO_F32_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.F32.xisf") } - protected val PALETTE_MONO_F64_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.F64.xisf") } - - protected val PALETTE_COLOR_8_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.8.xisf") } - protected val PALETTE_COLOR_16_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.16.xisf") } - protected val PALETTE_COLOR_32_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.32.xisf") } - protected val PALETTE_COLOR_F32_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.F32.xisf") } - protected val PALETTE_COLOR_F64_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.F64.xisf") } - - protected val DEBAYER_FITS by lazy { download("$GITHUB_FITS_URL/Debayer.fits") } - protected val M6707HH by lazy { download("$ASTROPY_PHOTOMETRY_URL/M6707HH.fits") } - protected val M31_FITS by lazy { download("00 42 44.3".hours, "41 16 9".deg, 3.deg) } - - protected val STAR_FOCUS_1 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.1.fits") } - protected val STAR_FOCUS_2 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.2.fits") } - protected val STAR_FOCUS_3 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.3.fits") } - protected val STAR_FOCUS_4 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.4.fits") } - protected val STAR_FOCUS_5 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.5.fits") } - protected val STAR_FOCUS_6 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.6.fits") } - protected val STAR_FOCUS_7 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.7.fits") } - protected val STAR_FOCUS_8 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.8.fits") } - protected val STAR_FOCUS_9 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.9.fits") } - protected val STAR_FOCUS_10 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.10.fits") } - protected val STAR_FOCUS_11 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.11.fits") } - protected val STAR_FOCUS_12 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.12.fits") } - protected val STAR_FOCUS_13 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.13.fits") } - protected val STAR_FOCUS_14 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.14.fits") } - protected val STAR_FOCUS_15 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.15.fits") } - protected val STAR_FOCUS_16 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.16.fits") } - protected val STAR_FOCUS_17 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.17.fits") } - - protected val PI_01_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.01.LIGHT.fits") } - protected val PI_02_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.02.LIGHT.fits") } - protected val PI_03_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.03.LIGHT.fits") } - protected val PI_04_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.04.LIGHT.fits") } - protected val PI_05_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.05.LIGHT.fits") } - protected val PI_06_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.06.LIGHT.fits") } - protected val PI_07_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.07.LIGHT.fits") } - protected val PI_08_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.08.LIGHT.fits") } - protected val PI_09_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.09.LIGHT.fits") } - protected val PI_10_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.10.LIGHT.fits") } - protected val PI_BIAS by lazy { download("$GITHUB_FITS_URL/PI.BIAS.fits") } - protected val PI_DARK by lazy { download("$GITHUB_FITS_URL/PI.DARK.fits") } - protected val PI_FLAT by lazy { download("$GITHUB_FITS_URL/PI.FLAT.fits") } - - protected val PI_FOCUS_0 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.0.fits") } - protected val PI_FOCUS_10000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.10000.fits") } - protected val PI_FOCUS_20000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.20000.fits") } - protected val PI_FOCUS_30000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.30000.fits") } - protected val PI_FOCUS_100000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.100000.fits") } - - private val afterEach = AfterEach() - - init { - prependExtension(afterEach) - } - - protected fun TestScope.closeAfterEach(closeable: T) = closeable.apply { - afterEach.add(testCase to this) - } - - protected fun BufferedImage.save(name: String): Pair { - val path = Path.of("..", "data", "test", "$name.png").createParentDirectories() - ImageIO.write(this, "PNG", path.toFile()) - val md5 = path.md5() - println("$name: $md5") - return path to md5 - } - - protected fun ByteArray.md5() = toByteString().md5().hex() - - protected fun Path.md5() = readBytes().md5() - - protected val String.extensionFromUrl - get() = if (endsWith(".fits", true)) "fits" - else if (endsWith(".xisf", true)) "xisf" - else "" - - protected fun download(url: String, extension: String = url.extensionFromUrl): Path { - require(extension.isNotBlank()) - - return synchronize(url) { - val name = url.toByteArray().md5() - val path = Path.of(System.getProperty("java.io.tmpdir"), "$name.$extension") - - if (!path.exists() || path.fileSize() <= 0L) { - val request = Request.Builder().get().url(url).build() - val call = HTTP_CLIENT.newCall(request) - - call.execute().use { - it.body?.byteStream()?.transferAndCloseOutput(path.outputStream()) - } - } - - path - } - } - - protected fun download(centerRA: Angle, centerDEC: Angle, fov: Angle): Path { - val name = "$centerRA@$centerDEC@$fov".toByteArray().md5() - - return synchronize(name) { - val path = Path.of(System.getProperty("java.io.tmpdir"), name) - - if (!path.exists() || path.fileSize() <= 0L) { - HIPS_SERVICE - .query(CDS_P_DSS2_NIR.id, centerRA, centerDEC, 1280, 720, 0.0, fov) - .execute() - .body()!! - .use { it.byteStream().transferAndCloseOutput(path.outputStream()) } - } - - path - } - } - - protected fun ImageHdu.makeImage(): BufferedImage { - val type = if (numberOfChannels == 1) BufferedImage.TYPE_BYTE_GRAY else BufferedImage.TYPE_INT_RGB - val image = BufferedImage(width, height, type) - val numberOfPixels = data.numberOfPixels - - if (numberOfChannels == 1) { - val buffer = (image.raster.dataBuffer as DataBufferByte).data - - repeat(numberOfPixels) { - buffer[it] = (data.red[it] * 255f).toInt().toByte() - } - } else { - val buffer = (image.raster.dataBuffer as DataBufferInt).data - - repeat(numberOfPixels) { - val red = (data.red[it] * 255f).toInt() and 0xFF - val green = (data.green[it] * 255f).toInt() and 0xFF - val blue = (data.blue[it] * 255f).toInt() and 0xFF - buffer[it] = blue or (green shl 8) or (red shl 16) - } - } - - return image - } - - @Suppress("BlockingMethodInNonBlockingContext") - private class AfterEach : ConcurrentLinkedDeque>(), TestListener { - - override suspend fun afterEach(testCase: TestCase, result: TestResult) { - find { it.first === testCase }?.second?.close() ?: return - } - } - - companion object { - - const val ASTROPY_PHOTOMETRY_URL = "https://www.astropy.org/astropy-data/photometry" - const val GITHUB_FITS_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/test/fits" - const val GITHUB_XISF_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/test/xisf" - - @JvmStatic val HTTP_CLIENT = OkHttpClient.Builder() - .readTimeout(60L, TimeUnit.SECONDS) - .writeTimeout(60L, TimeUnit.SECONDS) - .connectTimeout(60L, TimeUnit.SECONDS) - .callTimeout(60L, TimeUnit.SECONDS) - .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) - .build() - - @JvmStatic val HIPS_SERVICE = Hips2FitsService(httpClient = HTTP_CLIENT) - @JvmStatic val CDS_P_DSS2_NIR = HipsSurvey("CDS/P/DSS2/NIR") - @JvmStatic @PublishedApi internal val SYNC_KEYS = ConcurrentHashMap() - - inline fun synchronize(key: String, block: () -> R): R { - val lock = synchronized(SYNC_KEYS) { SYNC_KEYS.getOrPut(key) { Any() } } - return synchronized(lock, block) - } - } -} diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/AbstractTest.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/AbstractTest.kt new file mode 100644 index 000000000..de32a5719 --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/AbstractTest.kt @@ -0,0 +1,46 @@ +package nebulosa.test + +import org.junit.jupiter.api.AfterEach +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.deleteIfExists +import kotlin.io.path.deleteRecursively +import kotlin.io.path.isDirectory + +abstract class AbstractTest { + + private val autoCloseables = HashSet(2) + private val deletablePaths = ArrayList(2) + + @AfterEach + fun closeResourcesAfterEach() { + autoCloseables.forEach(AutoCloseable::close) + autoCloseables.clear() + } + + @AfterEach + fun deletePathsAfterEach() { + deletablePaths.forEach { it.deleteRecursivelyOrIfExists() } + deletablePaths.clear() + } + + protected fun T.autoClose(): T { + return apply(autoCloseables::add) + } + + protected fun Path.deleteRecursivelyOrIfExists() { + if (isDirectory()) deleteRecursively() else deleteIfExists() + } + + protected fun Path.deleteAfterEach(): Path { + return apply(deletablePaths::add) + } + + protected fun tempPath(prefix: String, suffix: String): Path { + return Files.createTempFile(prefix, suffix).deleteAfterEach() + } + + protected fun tempDirectory(prefix: String): Path { + return Files.createTempDirectory(prefix).deleteAfterEach() + } +} diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/Extensions.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/Extensions.kt new file mode 100644 index 000000000..708cf0d2b --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/Extensions.kt @@ -0,0 +1,20 @@ +@file:JvmName("Extensions") +@file:Suppress("NOTHING_TO_INLINE") + +package nebulosa.test + +import okio.ByteString.Companion.toByteString +import java.awt.image.BufferedImage +import java.nio.file.Path +import javax.imageio.ImageIO +import kotlin.io.path.createParentDirectories +import kotlin.io.path.readBytes + +inline fun ByteArray.md5() = toByteString().md5().hex() +inline fun Path.md5() = readBytes().md5() // TODO: Improve it. Remove readBytes() + +fun BufferedImage.save(name: String): Pair { + val path = dataDirectory.concat("test", "$name.png").createParentDirectories() + ImageIO.write(this, "PNG", path.toFile()) + return path to path.md5().also { println("$name: $it") } +} diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt new file mode 100644 index 000000000..ce19039cf --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/Files.kt @@ -0,0 +1,109 @@ +@file:JvmName("Files") + +package nebulosa.test + +import nebulosa.math.deg +import nebulosa.math.hours + +const val ASTROPY_PHOTOMETRY_URL = "https://www.astropy.org/astropy-data/photometry" +const val GITHUB_FITS_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/test/fits" +const val GITHUB_XISF_URL = "https://github.com/tiagohm/nebulosa.data/raw/main/test/xisf" + +val M82_MONO_8_LZ4_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.LZ4.xisf") } +val M82_MONO_8_LZ4_HC_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.LZ4-HC.xisf") } +val M82_MONO_8_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.xisf") } +val M82_MONO_8_ZLIB_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.ZLib.xisf") } +val M82_MONO_8_ZSTANDARD_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.8.ZStandard.xisf") } +val M82_MONO_16_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.16.xisf") } +val M82_MONO_32_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.32.xisf") } +val M82_MONO_F32_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.F32.xisf") } +val M82_MONO_F64_XISF by lazy { download("$GITHUB_XISF_URL/M82.Mono.F64.xisf") } + +val M82_COLOR_8_LZ4_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.LZ4.xisf") } +val M82_COLOR_8_LZ4_HC_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.LZ4-HC.xisf") } +val M82_COLOR_8_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.xisf") } +val M82_COLOR_8_ZLIB_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.ZLib.xisf") } +val M82_COLOR_8_ZSTANDARD_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.8.ZStandard.xisf") } +val M82_COLOR_16_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.16.xisf") } +val M82_COLOR_32_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.32.xisf") } +val M82_COLOR_F32_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.F32.xisf") } +val M82_COLOR_F64_XISF by lazy { download("$GITHUB_XISF_URL/M82.Color.F64.xisf") } +val DEBAYER_XISF_PATH by lazy { download("$GITHUB_XISF_URL/Debayer.xisf") } + +val NGC3344_MONO_8_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.8.fits") } +val NGC3344_MONO_16_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.16.fits") } +val NGC3344_MONO_32_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.32.fits") } +val NGC3344_MONO_F32_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.F32.fits") } +val NGC3344_MONO_F64_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Mono.F64.fits") } + +val NGC3344_COLOR_8_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.8.fits") } +val NGC3344_COLOR_16_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.16.fits") } +val NGC3344_COLOR_32_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.32.fits") } +val NGC3344_COLOR_F32_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.F32.fits") } +val NGC3344_COLOR_F64_FITS by lazy { download("$GITHUB_FITS_URL/NGC3344.Color.F64.fits") } + +val PALETTE_MONO_8_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.8.fits") } +val PALETTE_MONO_16_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.16.fits") } +val PALETTE_MONO_32_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.32.fits") } +val PALETTE_MONO_F32_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.F32.fits") } +val PALETTE_MONO_F64_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Mono.F64.fits") } + +val PALETTE_COLOR_8_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.8.fits") } +val PALETTE_COLOR_16_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.16.fits") } +val PALETTE_COLOR_32_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.32.fits") } +val PALETTE_COLOR_F32_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.F32.fits") } +val PALETTE_COLOR_F64_FITS by lazy { download("$GITHUB_FITS_URL/PALETTE.Color.F64.fits") } + +val PALETTE_MONO_8_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.8.xisf") } +val PALETTE_MONO_16_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.16.xisf") } +val PALETTE_MONO_32_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.32.xisf") } +val PALETTE_MONO_F32_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.F32.xisf") } +val PALETTE_MONO_F64_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Mono.F64.xisf") } + +val PALETTE_COLOR_8_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.8.xisf") } +val PALETTE_COLOR_16_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.16.xisf") } +val PALETTE_COLOR_32_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.32.xisf") } +val PALETTE_COLOR_F32_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.F32.xisf") } +val PALETTE_COLOR_F64_XISF by lazy { download("$GITHUB_XISF_URL/PALETTE.Color.F64.xisf") } + +val DEBAYER_FITS by lazy { download("$GITHUB_FITS_URL/Debayer.fits") } +val M6707HH by lazy { download("$ASTROPY_PHOTOMETRY_URL/M6707HH.fits") } +val M31_FITS by lazy { downloadFits("00 42 44.3".hours, "41 16 9".deg, 3.deg) } + +val STAR_FOCUS_1 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.1.fits") } +val STAR_FOCUS_2 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.2.fits") } +val STAR_FOCUS_3 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.3.fits") } +val STAR_FOCUS_4 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.4.fits") } +val STAR_FOCUS_5 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.5.fits") } +val STAR_FOCUS_6 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.6.fits") } +val STAR_FOCUS_7 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.7.fits") } +val STAR_FOCUS_8 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.8.fits") } +val STAR_FOCUS_9 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.9.fits") } +val STAR_FOCUS_10 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.10.fits") } +val STAR_FOCUS_11 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.11.fits") } +val STAR_FOCUS_12 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.12.fits") } +val STAR_FOCUS_13 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.13.fits") } +val STAR_FOCUS_14 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.14.fits") } +val STAR_FOCUS_15 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.15.fits") } +val STAR_FOCUS_16 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.16.fits") } +val STAR_FOCUS_17 by lazy { download("$GITHUB_FITS_URL/STAR.FOCUS.17.fits") } + +val PI_01_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.01.LIGHT.fits") } +val PI_02_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.02.LIGHT.fits") } +val PI_03_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.03.LIGHT.fits") } +val PI_04_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.04.LIGHT.fits") } +val PI_05_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.05.LIGHT.fits") } +val PI_06_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.06.LIGHT.fits") } +val PI_07_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.07.LIGHT.fits") } +val PI_08_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.08.LIGHT.fits") } +val PI_09_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.09.LIGHT.fits") } +val PI_10_LIGHT by lazy { download("$GITHUB_FITS_URL/PI.10.LIGHT.fits") } +val PI_BIAS by lazy { download("$GITHUB_FITS_URL/PI.BIAS.fits") } +val PI_DARK by lazy { download("$GITHUB_FITS_URL/PI.DARK.fits") } +val PI_FLAT by lazy { download("$GITHUB_FITS_URL/PI.FLAT.fits") } + +val PI_FOCUS_0 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.0.fits") } +val PI_FOCUS_10000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.10000.fits") } +val PI_FOCUS_20000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.20000.fits") } +val PI_FOCUS_30000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.30000.fits") } +val PI_FOCUS_100000 by lazy { download("$GITHUB_FITS_URL/PI.FOCUS.100000.fits") } diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/Http.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/Http.kt new file mode 100644 index 000000000..bf45e0490 --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/Http.kt @@ -0,0 +1,66 @@ +@file:JvmName("Http") +@file:Suppress("NOTHING_TO_INLINE") + +package nebulosa.test + +import nebulosa.hips2fits.Hips2FitsService +import nebulosa.hips2fits.HipsSurvey +import nebulosa.io.transferAndCloseOutput +import nebulosa.math.Angle +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.logging.HttpLoggingInterceptor +import java.nio.file.Path +import java.util.concurrent.TimeUnit +import kotlin.io.path.exists +import kotlin.io.path.fileSize +import kotlin.io.path.outputStream + +val HTTP_CLIENT = OkHttpClient.Builder() + .readTimeout(60L, TimeUnit.SECONDS) + .writeTimeout(60L, TimeUnit.SECONDS) + .connectTimeout(60L, TimeUnit.SECONDS) + .callTimeout(60L, TimeUnit.SECONDS) + .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) + .build() + +private val String.extensionFromUrl + get() = lastIndexOf('.').let { if (it >= 0) substring(it + 1) else this } + +fun download(url: String, extension: String = url.extensionFromUrl): Path { + require(extension.isNotBlank()) + + return synchronized(url) { + val name = url.encodeToByteArray().md5() + val path = cacheDirectory.concat("$name.$extension") + + if (!path.exists() || path.fileSize() <= 0L) { + val request = Request.Builder().get().url(url).build() + val call = HTTP_CLIENT.newCall(request) + + call.execute().use { + it.body?.byteStream()?.transferAndCloseOutput(path.outputStream()) + } + } + + path + } +} + +private val HIPS_SERVICE = Hips2FitsService(httpClient = HTTP_CLIENT) +private val CDS_P_DSS2_NIR = HipsSurvey("CDS/P/DSS2/NIR") + +fun downloadFits(centerRA: Angle, centerDEC: Angle, fov: Angle): Path { + val name = "$centerRA@$centerDEC@$fov".toByteArray().md5() + val path = cacheDirectory.concat(name) + + if (!path.exists() || path.fileSize() <= 0L) { + HIPS_SERVICE + .query(CDS_P_DSS2_NIR.id, centerRA, centerDEC, 1280, 720, 0.0, fov) + .execute() + .body()!! + .use { it.byteStream().transferAndCloseOutput(path.outputStream()) } + } + + return path +} diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/LinuxOnly.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/LinuxOnly.kt new file mode 100644 index 000000000..7e8dc5c96 --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/LinuxOnly.kt @@ -0,0 +1,11 @@ +package nebulosa.test + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.EnabledOnOs +import org.junit.jupiter.api.condition.OS + +@Test +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +@EnabledOnOs(OS.LINUX) +annotation class LinuxOnly diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/Matchers.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/Matchers.kt new file mode 100644 index 000000000..0bfadb132 --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/Matchers.kt @@ -0,0 +1,4 @@ +@file:JvmName("Matchers") +@file:Suppress("NOTHING_TO_INLINE") + +package nebulosa.test diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnly.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnly.kt new file mode 100644 index 000000000..c41bf4b14 --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnly.kt @@ -0,0 +1,10 @@ +package nebulosa.test + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.EnabledIfSystemProperty + +@Test +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +@EnabledIfSystemProperty(named = "github", matches = "false") +annotation class NonGitHubOnly diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnlyCondition.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnlyCondition.kt deleted file mode 100644 index c5a598bd0..000000000 --- a/nebulosa-test/src/main/kotlin/nebulosa/test/NonGitHubOnlyCondition.kt +++ /dev/null @@ -1,12 +0,0 @@ -package nebulosa.test - -import io.kotest.core.annotation.EnabledCondition -import io.kotest.core.spec.Spec -import kotlin.reflect.KClass - -class NonGitHubOnlyCondition : EnabledCondition { - - override fun enabled(kclass: KClass): Boolean { - return System.getProperty("github", "false") == "false" - } -} diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/Paths.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/Paths.kt new file mode 100644 index 000000000..2b61ecb5c --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/Paths.kt @@ -0,0 +1,25 @@ +@file:Suppress("NOTHING_TO_INLINE") +@file:JvmName("Paths") + +package nebulosa.test + +import java.nio.file.Path + +val homeDirectory: Path + get() = Path.of(System.getProperty("user.home")) + +val rootDirectory: Path + get() = Path.of(System.getProperty("root.dir")) + +val projectDirectory: Path + get() = Path.of(System.getProperty("project.dir")) + +val dataDirectory: Path + get() = Path.of("$rootDirectory", "data") + +val cacheDirectory: Path + get() = Path.of("$rootDirectory", ".cache") + +inline fun Path.concat(path: String): Path = Path.of("$this", path) + +inline fun Path.concat(vararg path: String): Path = Path.of("$this", *path) diff --git a/nebulosa-test/src/main/kotlin/nebulosa/test/WindowsOnly.kt b/nebulosa-test/src/main/kotlin/nebulosa/test/WindowsOnly.kt new file mode 100644 index 000000000..881f2e8de --- /dev/null +++ b/nebulosa-test/src/main/kotlin/nebulosa/test/WindowsOnly.kt @@ -0,0 +1,11 @@ +package nebulosa.test + +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.EnabledOnOs +import org.junit.jupiter.api.condition.OS + +@Test +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +@EnabledOnOs(OS.WINDOWS) +annotation class WindowsOnly diff --git a/nebulosa-test/src/test/kotlin/PathsTest.kt b/nebulosa-test/src/test/kotlin/PathsTest.kt new file mode 100644 index 000000000..40ee672f9 --- /dev/null +++ b/nebulosa-test/src/test/kotlin/PathsTest.kt @@ -0,0 +1,27 @@ +import io.kotest.matchers.string.shouldEndWith +import nebulosa.test.* +import org.junit.jupiter.api.Test + +@LinuxOnly +class PathsTest { + + @Test + fun rootDirectory() { + "$rootDirectory" shouldEndWith "/nebulosa" + } + + @Test + fun projectDirectory() { + "$projectDirectory" shouldEndWith "/nebulosa-test" + } + + @Test + fun cacheDirectory() { + "$cacheDirectory" shouldEndWith "/.cache" + } + + @Test + fun dataDirectory() { + "$dataDirectory" shouldEndWith "/data" + } +} diff --git a/nebulosa-test/src/test/kotlin/SystemPropertyTest.kt b/nebulosa-test/src/test/kotlin/SystemPropertyTest.kt new file mode 100644 index 000000000..1d3218006 --- /dev/null +++ b/nebulosa-test/src/test/kotlin/SystemPropertyTest.kt @@ -0,0 +1,23 @@ +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.string.shouldEndWith +import nebulosa.test.LinuxOnly +import org.junit.jupiter.api.Test + +@LinuxOnly +class SystemPropertyTest { + + @Test + fun rootDir() { + System.getProperty("root.dir").shouldNotBeNull() shouldEndWith ("/nebulosa") + } + + @Test + fun projectDir() { + System.getProperty("project.dir").shouldNotBeNull() shouldEndWith ("/nebulosa-test") + } + + @Test + fun github() { + System.getProperty("github").shouldNotBeNull() + } +} diff --git a/nebulosa-time/src/test/kotlin/DeltaTTest.kt b/nebulosa-time/src/test/kotlin/DeltaTTest.kt index 9920849db..7e68a74e4 100644 --- a/nebulosa-time/src/test/kotlin/DeltaTTest.kt +++ b/nebulosa-time/src/test/kotlin/DeltaTTest.kt @@ -1,19 +1,18 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.time.DeltaT import nebulosa.time.TimeDelta import nebulosa.time.TimeYMDHMS +import org.junit.jupiter.api.Test -class DeltaTTest : StringSpec(), TimeDelta by DeltaT { +class DeltaTTest : TimeDelta by DeltaT { - init { - "delta" { - delta(TimeYMDHMS(-720)) shouldBe (20370.94276516515 plusOrMinus 1e-4) - delta(TimeYMDHMS(1170)) shouldBe (997.1912592573422 plusOrMinus 1e-4) - delta(TimeYMDHMS(1980)) shouldBe (50.53915198016422 plusOrMinus 1e-4) - delta(TimeYMDHMS(2023)) shouldBe (69.203827 plusOrMinus 1e-4) - delta(TimeYMDHMS(2600)) shouldBe (1623.7769924989768 plusOrMinus 1e-4) - } + @Test + fun delta() { + delta(TimeYMDHMS(-720)) shouldBe (20370.94276516515 plusOrMinus 1e-4) + delta(TimeYMDHMS(1170)) shouldBe (997.1912592573422 plusOrMinus 1e-4) + delta(TimeYMDHMS(1980)) shouldBe (50.53915198016422 plusOrMinus 1e-4) + delta(TimeYMDHMS(2023)) shouldBe (69.203827 plusOrMinus 1e-4) + delta(TimeYMDHMS(2600)) shouldBe (1623.7769924989768 plusOrMinus 1e-4) } } diff --git a/nebulosa-time/src/test/kotlin/IERSTest.kt b/nebulosa-time/src/test/kotlin/IERSTest.kt index 29a440795..a91e6019e 100644 --- a/nebulosa-time/src/test/kotlin/IERSTest.kt +++ b/nebulosa-time/src/test/kotlin/IERSTest.kt @@ -1,67 +1,76 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.* -import java.nio.file.Path +import org.junit.jupiter.api.Test import kotlin.io.path.inputStream -class IERSTest : StringSpec() { +class IERSTest { - init { - val iersa = IERSA() - iersa.load(Path.of("../data/finals2000A.all").inputStream()) + @Test + fun iersa() { + IERS.attach(IERSA) - val iersb = IERSB() - iersb.load(Path.of("../data/eopc04.1962-now.txt").inputStream()) + with(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-18) + fraction shouldBe (-1.2784074073774842e-06 plusOrMinus 1E-18) + } + with(UTC(TimeYMDHMS(2026, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2461042.0 plusOrMinus 1E-18) + fraction shouldBe (4.693171296595673e-07 plusOrMinus 1E-18) + } + with(UTC(TimeYMDHMS(1964, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2438396.0 plusOrMinus 1E-18) + fraction shouldBe (9.353564814864956e-06 plusOrMinus 1E-8) + } + } - "A" { - IERS.attach(iersa) + @Test + fun iersb() { + IERS.attach(IERSB) - with(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-18) - fraction shouldBe (-1.2784074073774842e-06 plusOrMinus 1E-18) - } - with(UTC(TimeYMDHMS(2026, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2461042.0 plusOrMinus 1E-18) - fraction shouldBe (4.693171296595673e-07 plusOrMinus 1E-18) - } - with(UTC(TimeYMDHMS(1964, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2438396.0 plusOrMinus 1E-18) - fraction shouldBe (9.353564814864956e-06 plusOrMinus 1E-8) - } + with(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-18) + fraction shouldBe (-1.2782876157107722e-06 plusOrMinus 1E-18) } - "B" { - IERS.attach(iersb) + with(UTC(TimeYMDHMS(2026, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2461042.0 plusOrMinus 1E-18) + fraction shouldBe (1.442650463262399e-07 plusOrMinus 1E-18) + } + with(UTC(TimeYMDHMS(1964, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2438396.0 plusOrMinus 1E-18) + fraction shouldBe (-9.322685184683761e-07 plusOrMinus 1E-8) + } + } - with(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-18) - fraction shouldBe (-1.2782876157107722e-06 plusOrMinus 1E-18) - } - with(UTC(TimeYMDHMS(2026, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2461042.0 plusOrMinus 1E-18) - fraction shouldBe (1.442650463262399e-07 plusOrMinus 1E-18) - } - with(UTC(TimeYMDHMS(1964, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2438396.0 plusOrMinus 1E-18) - fraction shouldBe (-9.322685184683761e-07 plusOrMinus 1E-8) - } + @Test + fun iersab() { + val iersab = IERSAB(IERSA, IERSB) + IERS.attach(iersab) + + with(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-18) + fraction shouldBe (-1.2782876157107722e-06 plusOrMinus 1E-18) + } + with(UTC(TimeYMDHMS(2026, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2461042.0 plusOrMinus 1E-18) + fraction shouldBe (4.693171296595673e-07 plusOrMinus 1E-18) + } + with(UTC(TimeYMDHMS(1964, 1, 1, 12, 0, 0.0)).ut1) { + whole shouldBe (2438396.0 plusOrMinus 1E-18) + fraction shouldBe (-9.322685184683761e-07 plusOrMinus 1E-8) } - "AB" { - val iersab = IERSAB(iersa, iersb) - IERS.attach(iersab) + } + + companion object { + + @JvmStatic private val IERSA = IERSA() + @JvmStatic private val IERSB = IERSB() - with(UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-18) - fraction shouldBe (-1.2782876157107722e-06 plusOrMinus 1E-18) - } - with(UTC(TimeYMDHMS(2026, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2461042.0 plusOrMinus 1E-18) - fraction shouldBe (4.693171296595673e-07 plusOrMinus 1E-18) - } - with(UTC(TimeYMDHMS(1964, 1, 1, 12, 0, 0.0)).ut1) { - whole shouldBe (2438396.0 plusOrMinus 1E-18) - fraction shouldBe (-9.322685184683761e-07 plusOrMinus 1E-8) - } + init { + dataDirectory.concat("finals2000A.all").inputStream().use(IERSA::load) + dataDirectory.concat("eopc04.1962-now.txt").inputStream().use(IERSB::load) } } } diff --git a/nebulosa-time/src/test/kotlin/InstantOfTimeTest.kt b/nebulosa-time/src/test/kotlin/InstantOfTimeTest.kt index 18b167310..36f331232 100644 --- a/nebulosa-time/src/test/kotlin/InstantOfTimeTest.kt +++ b/nebulosa-time/src/test/kotlin/InstantOfTimeTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe @@ -7,44 +6,50 @@ import nebulosa.math.hours import nebulosa.math.toDegrees import nebulosa.math.toHours import nebulosa.time.TimeYMDHMS +import org.junit.jupiter.api.Test -class InstantOfTimeTest : StringSpec() { +class InstantOfTimeTest { - init { - // https://dc.zah.uni-heidelberg.de/apfs/times/q/form + // https://dc.zah.uni-heidelberg.de/apfs/times/q/form - "gast" { - val angle = "06 39 30.2996".hours - TimeYMDHMS(2023, 1, 30, 22).gast.toHours shouldBe (angle.toHours plusOrMinus 1e-8) - } - "gmst" { - val angle = "06 39 30.8663".hours - TimeYMDHMS(2023, 1, 30, 22).gmst.toHours shouldBe (angle.toHours plusOrMinus 1e-8) - } - "era" { - val angle = "99 34 58.365".deg - TimeYMDHMS(2023, 1, 30, 22).era.toDegrees shouldBe (angle.toDegrees plusOrMinus 1e-6) - } - "datetime" { - var ymdhms = TimeYMDHMS(2023, 1, 30, 22, 45, 33.5) - var datetime = ymdhms.asDateTime() - datetime.year shouldBeExactly 2023 - datetime.monthValue shouldBeExactly 1 - datetime.dayOfMonth shouldBeExactly 30 - datetime.hour shouldBeExactly 22 - datetime.minute shouldBeExactly 45 - datetime.second shouldBeExactly 33 - datetime.nano shouldBeExactly 500000000 + @Test + fun gast() { + val angle = "06 39 30.2996".hours + TimeYMDHMS(2023, 1, 30, 22).gast.toHours shouldBe (angle.toHours plusOrMinus 1e-8) + } + + @Test + fun gmst() { + val angle = "06 39 30.8663".hours + TimeYMDHMS(2023, 1, 30, 22).gmst.toHours shouldBe (angle.toHours plusOrMinus 1e-8) + } + + @Test + fun era() { + val angle = "99 34 58.365".deg + TimeYMDHMS(2023, 1, 30, 22).era.toDegrees shouldBe (angle.toDegrees plusOrMinus 1e-6) + } + + @Test + fun datetime() { + var ymdhms = TimeYMDHMS(2023, 1, 30, 22, 45, 33.5) + var datetime = ymdhms.asDateTime() + datetime.year shouldBeExactly 2023 + datetime.monthValue shouldBeExactly 1 + datetime.dayOfMonth shouldBeExactly 30 + datetime.hour shouldBeExactly 22 + datetime.minute shouldBeExactly 45 + datetime.second shouldBeExactly 33 + datetime.nano shouldBeExactly 500000000 - ymdhms = TimeYMDHMS(2023, 1, 30, 0, 0, 0.0) - datetime = ymdhms.asDateTime() - datetime.year shouldBeExactly 2023 - datetime.monthValue shouldBeExactly 1 - datetime.dayOfMonth shouldBeExactly 30 - datetime.hour shouldBeExactly 0 - datetime.minute shouldBeExactly 0 - datetime.second shouldBeExactly 0 - datetime.nano shouldBeExactly 0 - } + ymdhms = TimeYMDHMS(2023, 1, 30, 0, 0, 0.0) + datetime = ymdhms.asDateTime() + datetime.year shouldBeExactly 2023 + datetime.monthValue shouldBeExactly 1 + datetime.dayOfMonth shouldBeExactly 30 + datetime.hour shouldBeExactly 0 + datetime.minute shouldBeExactly 0 + datetime.second shouldBeExactly 0 + datetime.nano shouldBeExactly 0 } } diff --git a/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrison2004Test.kt b/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrison2004Test.kt index d95019521..c9552cf86 100644 --- a/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrison2004Test.kt +++ b/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrison2004Test.kt @@ -1,79 +1,80 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.time.ParabolaOfStephensonMorrison2004 import nebulosa.time.Spline +import org.junit.jupiter.api.Test -class ParabolaOfStephensonMorrison2004Test : StringSpec(), Spline by ParabolaOfStephensonMorrison2004 { +class ParabolaOfStephensonMorrison2004Test : Spline by ParabolaOfStephensonMorrison2004 { - init { - "parabola" { - compute(0.0) shouldBe (10579.679999999998 plusOrMinus 1E-6) - compute(100.0) shouldBe (9446.88 plusOrMinus 1E-6) - compute(200.0) shouldBe (8378.08 plusOrMinus 1E-6) - compute(300.0) shouldBe (7373.28 plusOrMinus 1E-6) - compute(400.0) shouldBe (6432.48 plusOrMinus 1E-6) - compute(500.0) shouldBe (5555.679999999999 plusOrMinus 1E-6) - compute(600.0) shouldBe (4742.879999999999 plusOrMinus 1E-6) - compute(700.0) shouldBe (3994.0799999999995 plusOrMinus 1E-6) - compute(800.0) shouldBe (3309.2799999999997 plusOrMinus 1E-6) - compute(900.0) shouldBe (2688.4799999999996 plusOrMinus 1E-6) - compute(1000.0) shouldBe (2131.68 plusOrMinus 1E-6) - compute(1100.0) shouldBe (1638.88 plusOrMinus 1E-6) - compute(1200.0) shouldBe (1210.0800000000002 plusOrMinus 1E-6) - compute(1300.0) shouldBe (845.2800000000001 plusOrMinus 1E-6) - compute(1400.0) shouldBe (544.48 plusOrMinus 1E-6) - compute(1500.0) shouldBe (307.68000000000006 plusOrMinus 1E-6) - compute(1600.0) shouldBe (134.88000000000002 plusOrMinus 1E-6) - compute(1700.0) shouldBe (26.08 plusOrMinus 1E-6) - compute(1800.0) shouldBe (-18.72 plusOrMinus 1E-6) - compute(1900.0) shouldBe (0.480000000000004 plusOrMinus 1E-6) - compute(2000.0) shouldBe (83.68 plusOrMinus 1E-6) - compute(2100.0) shouldBe (230.87999999999997 plusOrMinus 1E-6) - compute(2200.0) shouldBe (442.08 plusOrMinus 1E-6) - compute(2300.0) shouldBe (717.28 plusOrMinus 1E-6) - compute(2400.0) shouldBe (1056.48 plusOrMinus 1E-6) - compute(2500.0) shouldBe (1459.6799999999998 plusOrMinus 1E-6) - compute(2600.0) shouldBe (1926.8799999999999 plusOrMinus 1E-6) - compute(2700.0) shouldBe (2458.0800000000004 plusOrMinus 1E-6) - compute(2800.0) shouldBe (3053.2800000000007 plusOrMinus 1E-6) - compute(2900.0) shouldBe (3712.4800000000005 plusOrMinus 1E-6) - compute(3000.0) shouldBe (4435.68 plusOrMinus 1E-6) - } - "derivative" { - with(derivative) { - compute(0.0) shouldBe (-11.648 plusOrMinus 1E-6) - compute(100.0) shouldBe (-11.008 plusOrMinus 1E-6) - compute(200.0) shouldBe (-10.368 plusOrMinus 1E-6) - compute(300.0) shouldBe (-9.728 plusOrMinus 1E-6) - compute(400.0) shouldBe (-9.088 plusOrMinus 1E-6) - compute(500.0) shouldBe (-8.448 plusOrMinus 1E-6) - compute(600.0) shouldBe (-7.808 plusOrMinus 1E-6) - compute(700.0) shouldBe (-7.167999999999999 plusOrMinus 1E-6) - compute(800.0) shouldBe (-6.528 plusOrMinus 1E-6) - compute(900.0) shouldBe (-5.888 plusOrMinus 1E-6) - compute(1000.0) shouldBe (-5.247999999999999 plusOrMinus 1E-6) - compute(1100.0) shouldBe (-4.6080000000000005 plusOrMinus 1E-6) - compute(1200.0) shouldBe (-3.9680000000000004 plusOrMinus 1E-6) - compute(1300.0) shouldBe (-3.3280000000000003 plusOrMinus 1E-6) - compute(1400.0) shouldBe (-2.688 plusOrMinus 1E-6) - compute(1500.0) shouldBe (-2.048 plusOrMinus 1E-6) - compute(1600.0) shouldBe (-1.4080000000000001 plusOrMinus 1E-6) - compute(1700.0) shouldBe (-0.768 plusOrMinus 1E-6) - compute(1800.0) shouldBe (-0.128 plusOrMinus 1E-6) - compute(1900.0) shouldBe (0.512 plusOrMinus 1E-6) - compute(2000.0) shouldBe (1.1520000000000001 plusOrMinus 1E-6) - compute(2100.0) shouldBe (1.7919999999999998 plusOrMinus 1E-6) - compute(2200.0) shouldBe (2.432 plusOrMinus 1E-6) - compute(2300.0) shouldBe (3.072 plusOrMinus 1E-6) - compute(2400.0) shouldBe (3.7119999999999997 plusOrMinus 1E-6) - compute(2500.0) shouldBe (4.352 plusOrMinus 1E-6) - compute(2600.0) shouldBe (4.992 plusOrMinus 1E-6) - compute(2700.0) shouldBe (5.632000000000001 plusOrMinus 1E-6) - compute(2800.0) shouldBe (6.272 plusOrMinus 1E-6) - compute(2900.0) shouldBe (6.912000000000001 plusOrMinus 1E-6) - compute(3000.0) shouldBe (7.5520000000000005 plusOrMinus 1E-6) - } + @Test + fun parabola() { + compute(0.0) shouldBe (10579.679999999998 plusOrMinus 1E-6) + compute(100.0) shouldBe (9446.88 plusOrMinus 1E-6) + compute(200.0) shouldBe (8378.08 plusOrMinus 1E-6) + compute(300.0) shouldBe (7373.28 plusOrMinus 1E-6) + compute(400.0) shouldBe (6432.48 plusOrMinus 1E-6) + compute(500.0) shouldBe (5555.679999999999 plusOrMinus 1E-6) + compute(600.0) shouldBe (4742.879999999999 plusOrMinus 1E-6) + compute(700.0) shouldBe (3994.0799999999995 plusOrMinus 1E-6) + compute(800.0) shouldBe (3309.2799999999997 plusOrMinus 1E-6) + compute(900.0) shouldBe (2688.4799999999996 plusOrMinus 1E-6) + compute(1000.0) shouldBe (2131.68 plusOrMinus 1E-6) + compute(1100.0) shouldBe (1638.88 plusOrMinus 1E-6) + compute(1200.0) shouldBe (1210.0800000000002 plusOrMinus 1E-6) + compute(1300.0) shouldBe (845.2800000000001 plusOrMinus 1E-6) + compute(1400.0) shouldBe (544.48 plusOrMinus 1E-6) + compute(1500.0) shouldBe (307.68000000000006 plusOrMinus 1E-6) + compute(1600.0) shouldBe (134.88000000000002 plusOrMinus 1E-6) + compute(1700.0) shouldBe (26.08 plusOrMinus 1E-6) + compute(1800.0) shouldBe (-18.72 plusOrMinus 1E-6) + compute(1900.0) shouldBe (0.480000000000004 plusOrMinus 1E-6) + compute(2000.0) shouldBe (83.68 plusOrMinus 1E-6) + compute(2100.0) shouldBe (230.87999999999997 plusOrMinus 1E-6) + compute(2200.0) shouldBe (442.08 plusOrMinus 1E-6) + compute(2300.0) shouldBe (717.28 plusOrMinus 1E-6) + compute(2400.0) shouldBe (1056.48 plusOrMinus 1E-6) + compute(2500.0) shouldBe (1459.6799999999998 plusOrMinus 1E-6) + compute(2600.0) shouldBe (1926.8799999999999 plusOrMinus 1E-6) + compute(2700.0) shouldBe (2458.0800000000004 plusOrMinus 1E-6) + compute(2800.0) shouldBe (3053.2800000000007 plusOrMinus 1E-6) + compute(2900.0) shouldBe (3712.4800000000005 plusOrMinus 1E-6) + compute(3000.0) shouldBe (4435.68 plusOrMinus 1E-6) + } + + @Test + fun derivative() { + with(derivative) { + compute(0.0) shouldBe (-11.648 plusOrMinus 1E-6) + compute(100.0) shouldBe (-11.008 plusOrMinus 1E-6) + compute(200.0) shouldBe (-10.368 plusOrMinus 1E-6) + compute(300.0) shouldBe (-9.728 plusOrMinus 1E-6) + compute(400.0) shouldBe (-9.088 plusOrMinus 1E-6) + compute(500.0) shouldBe (-8.448 plusOrMinus 1E-6) + compute(600.0) shouldBe (-7.808 plusOrMinus 1E-6) + compute(700.0) shouldBe (-7.167999999999999 plusOrMinus 1E-6) + compute(800.0) shouldBe (-6.528 plusOrMinus 1E-6) + compute(900.0) shouldBe (-5.888 plusOrMinus 1E-6) + compute(1000.0) shouldBe (-5.247999999999999 plusOrMinus 1E-6) + compute(1100.0) shouldBe (-4.6080000000000005 plusOrMinus 1E-6) + compute(1200.0) shouldBe (-3.9680000000000004 plusOrMinus 1E-6) + compute(1300.0) shouldBe (-3.3280000000000003 plusOrMinus 1E-6) + compute(1400.0) shouldBe (-2.688 plusOrMinus 1E-6) + compute(1500.0) shouldBe (-2.048 plusOrMinus 1E-6) + compute(1600.0) shouldBe (-1.4080000000000001 plusOrMinus 1E-6) + compute(1700.0) shouldBe (-0.768 plusOrMinus 1E-6) + compute(1800.0) shouldBe (-0.128 plusOrMinus 1E-6) + compute(1900.0) shouldBe (0.512 plusOrMinus 1E-6) + compute(2000.0) shouldBe (1.1520000000000001 plusOrMinus 1E-6) + compute(2100.0) shouldBe (1.7919999999999998 plusOrMinus 1E-6) + compute(2200.0) shouldBe (2.432 plusOrMinus 1E-6) + compute(2300.0) shouldBe (3.072 plusOrMinus 1E-6) + compute(2400.0) shouldBe (3.7119999999999997 plusOrMinus 1E-6) + compute(2500.0) shouldBe (4.352 plusOrMinus 1E-6) + compute(2600.0) shouldBe (4.992 plusOrMinus 1E-6) + compute(2700.0) shouldBe (5.632000000000001 plusOrMinus 1E-6) + compute(2800.0) shouldBe (6.272 plusOrMinus 1E-6) + compute(2900.0) shouldBe (6.912000000000001 plusOrMinus 1E-6) + compute(3000.0) shouldBe (7.5520000000000005 plusOrMinus 1E-6) } } } diff --git a/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrisonHohenkerk2016Test.kt b/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrisonHohenkerk2016Test.kt index e8e40f14e..34723e01a 100644 --- a/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrisonHohenkerk2016Test.kt +++ b/nebulosa-time/src/test/kotlin/ParabolaOfStephensonMorrisonHohenkerk2016Test.kt @@ -1,79 +1,80 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.time.ParabolaOfStephensonMorrisonHohenkerk2016 import nebulosa.time.Spline +import org.junit.jupiter.api.Test -class ParabolaOfStephensonMorrisonHohenkerk2016Test : StringSpec(), Spline by ParabolaOfStephensonMorrisonHohenkerk2016 { +class ParabolaOfStephensonMorrisonHohenkerk2016Test : Spline by ParabolaOfStephensonMorrisonHohenkerk2016 { - init { - "parabola" { - compute(0.0) shouldBe (10504.53125 plusOrMinus 1E-6) - compute(100.0) shouldBe (9350.78125 plusOrMinus 1E-6) - compute(200.0) shouldBe (8262.03125 plusOrMinus 1E-6) - compute(300.0) shouldBe (7238.28125 plusOrMinus 1E-6) - compute(400.0) shouldBe (6279.53125 plusOrMinus 1E-6) - compute(500.0) shouldBe (5385.78125 plusOrMinus 1E-6) - compute(600.0) shouldBe (4557.03125 plusOrMinus 1E-6) - compute(700.0) shouldBe (3793.28125 plusOrMinus 1E-6) - compute(800.0) shouldBe (3094.53125 plusOrMinus 1E-6) - compute(900.0) shouldBe (2460.78125 plusOrMinus 1E-6) - compute(1000.0) shouldBe (1892.03125 plusOrMinus 1E-6) - compute(1100.0) shouldBe (1388.28125 plusOrMinus 1E-6) - compute(1200.0) shouldBe (949.53125 plusOrMinus 1E-6) - compute(1300.0) shouldBe (575.78125 plusOrMinus 1E-6) - compute(1400.0) shouldBe (267.03125 plusOrMinus 1E-6) - compute(1500.0) shouldBe (23.28125 plusOrMinus 1E-6) - compute(1600.0) shouldBe (-155.46875 plusOrMinus 1E-6) - compute(1700.0) shouldBe (-269.21875 plusOrMinus 1E-6) - compute(1800.0) shouldBe (-317.96875 plusOrMinus 1E-6) - compute(1900.0) shouldBe (-301.71875 plusOrMinus 1E-6) - compute(2000.0) shouldBe (-220.46875 plusOrMinus 1E-6) - compute(2100.0) shouldBe (-74.21875 plusOrMinus 1E-6) - compute(2200.0) shouldBe (137.03125 plusOrMinus 1E-6) - compute(2300.0) shouldBe (413.28125 plusOrMinus 1E-6) - compute(2400.0) shouldBe (754.53125 plusOrMinus 1E-6) - compute(2500.0) shouldBe (1160.78125 plusOrMinus 1E-6) - compute(2600.0) shouldBe (1632.03125 plusOrMinus 1E-6) - compute(2700.0) shouldBe (2168.28125 plusOrMinus 1E-6) - compute(2800.0) shouldBe (2769.53125 plusOrMinus 1E-6) - compute(2900.0) shouldBe (3435.78125 plusOrMinus 1E-6) - compute(3000.0) shouldBe (4167.03125 plusOrMinus 1E-6) - } - "derivative" { - with(derivative) { - compute(0.0) shouldBe (-11.8625 plusOrMinus 1E-6) - compute(100.0) shouldBe (-11.2125 plusOrMinus 1E-6) - compute(200.0) shouldBe (-10.5625 plusOrMinus 1E-6) - compute(300.0) shouldBe (-9.9125 plusOrMinus 1E-6) - compute(400.0) shouldBe (-9.262500000000001 plusOrMinus 1E-6) - compute(500.0) shouldBe (-8.6125 plusOrMinus 1E-6) - compute(600.0) shouldBe (-7.9625 plusOrMinus 1E-6) - compute(700.0) shouldBe (-7.3125 plusOrMinus 1E-6) - compute(800.0) shouldBe (-6.6625000000000005 plusOrMinus 1E-6) - compute(900.0) shouldBe (-6.0125 plusOrMinus 1E-6) - compute(1000.0) shouldBe (-5.3625 plusOrMinus 1E-6) - compute(1100.0) shouldBe (-4.7125 plusOrMinus 1E-6) - compute(1200.0) shouldBe (-4.0625 plusOrMinus 1E-6) - compute(1300.0) shouldBe (-3.4125 plusOrMinus 1E-6) - compute(1400.0) shouldBe (-2.7625 plusOrMinus 1E-6) - compute(1500.0) shouldBe (-2.1125000000000003 plusOrMinus 1E-6) - compute(1600.0) shouldBe (-1.4625000000000001 plusOrMinus 1E-6) - compute(1700.0) shouldBe (-0.8125 plusOrMinus 1E-6) - compute(1800.0) shouldBe (-0.1625 plusOrMinus 1E-6) - compute(1900.0) shouldBe (0.48750000000000004 plusOrMinus 1E-6) - compute(2000.0) shouldBe (1.1375 plusOrMinus 1E-6) - compute(2100.0) shouldBe (1.7875 plusOrMinus 1E-6) - compute(2200.0) shouldBe (2.4375 plusOrMinus 1E-6) - compute(2300.0) shouldBe (3.0875 plusOrMinus 1E-6) - compute(2400.0) shouldBe (3.7375000000000003 plusOrMinus 1E-6) - compute(2500.0) shouldBe (4.3875 plusOrMinus 1E-6) - compute(2600.0) shouldBe (5.0375000000000005 plusOrMinus 1E-6) - compute(2700.0) shouldBe (5.6875 plusOrMinus 1E-6) - compute(2800.0) shouldBe (6.3375 plusOrMinus 1E-6) - compute(2900.0) shouldBe (6.9875 plusOrMinus 1E-6) - compute(3000.0) shouldBe (7.6375 plusOrMinus 1E-6) - } + @Test + fun parabola() { + compute(0.0) shouldBe (10504.53125 plusOrMinus 1E-6) + compute(100.0) shouldBe (9350.78125 plusOrMinus 1E-6) + compute(200.0) shouldBe (8262.03125 plusOrMinus 1E-6) + compute(300.0) shouldBe (7238.28125 plusOrMinus 1E-6) + compute(400.0) shouldBe (6279.53125 plusOrMinus 1E-6) + compute(500.0) shouldBe (5385.78125 plusOrMinus 1E-6) + compute(600.0) shouldBe (4557.03125 plusOrMinus 1E-6) + compute(700.0) shouldBe (3793.28125 plusOrMinus 1E-6) + compute(800.0) shouldBe (3094.53125 plusOrMinus 1E-6) + compute(900.0) shouldBe (2460.78125 plusOrMinus 1E-6) + compute(1000.0) shouldBe (1892.03125 plusOrMinus 1E-6) + compute(1100.0) shouldBe (1388.28125 plusOrMinus 1E-6) + compute(1200.0) shouldBe (949.53125 plusOrMinus 1E-6) + compute(1300.0) shouldBe (575.78125 plusOrMinus 1E-6) + compute(1400.0) shouldBe (267.03125 plusOrMinus 1E-6) + compute(1500.0) shouldBe (23.28125 plusOrMinus 1E-6) + compute(1600.0) shouldBe (-155.46875 plusOrMinus 1E-6) + compute(1700.0) shouldBe (-269.21875 plusOrMinus 1E-6) + compute(1800.0) shouldBe (-317.96875 plusOrMinus 1E-6) + compute(1900.0) shouldBe (-301.71875 plusOrMinus 1E-6) + compute(2000.0) shouldBe (-220.46875 plusOrMinus 1E-6) + compute(2100.0) shouldBe (-74.21875 plusOrMinus 1E-6) + compute(2200.0) shouldBe (137.03125 plusOrMinus 1E-6) + compute(2300.0) shouldBe (413.28125 plusOrMinus 1E-6) + compute(2400.0) shouldBe (754.53125 plusOrMinus 1E-6) + compute(2500.0) shouldBe (1160.78125 plusOrMinus 1E-6) + compute(2600.0) shouldBe (1632.03125 plusOrMinus 1E-6) + compute(2700.0) shouldBe (2168.28125 plusOrMinus 1E-6) + compute(2800.0) shouldBe (2769.53125 plusOrMinus 1E-6) + compute(2900.0) shouldBe (3435.78125 plusOrMinus 1E-6) + compute(3000.0) shouldBe (4167.03125 plusOrMinus 1E-6) + } + + @Test + fun derivative() { + with(derivative) { + compute(0.0) shouldBe (-11.8625 plusOrMinus 1E-6) + compute(100.0) shouldBe (-11.2125 plusOrMinus 1E-6) + compute(200.0) shouldBe (-10.5625 plusOrMinus 1E-6) + compute(300.0) shouldBe (-9.9125 plusOrMinus 1E-6) + compute(400.0) shouldBe (-9.262500000000001 plusOrMinus 1E-6) + compute(500.0) shouldBe (-8.6125 plusOrMinus 1E-6) + compute(600.0) shouldBe (-7.9625 plusOrMinus 1E-6) + compute(700.0) shouldBe (-7.3125 plusOrMinus 1E-6) + compute(800.0) shouldBe (-6.6625000000000005 plusOrMinus 1E-6) + compute(900.0) shouldBe (-6.0125 plusOrMinus 1E-6) + compute(1000.0) shouldBe (-5.3625 plusOrMinus 1E-6) + compute(1100.0) shouldBe (-4.7125 plusOrMinus 1E-6) + compute(1200.0) shouldBe (-4.0625 plusOrMinus 1E-6) + compute(1300.0) shouldBe (-3.4125 plusOrMinus 1E-6) + compute(1400.0) shouldBe (-2.7625 plusOrMinus 1E-6) + compute(1500.0) shouldBe (-2.1125000000000003 plusOrMinus 1E-6) + compute(1600.0) shouldBe (-1.4625000000000001 plusOrMinus 1E-6) + compute(1700.0) shouldBe (-0.8125 plusOrMinus 1E-6) + compute(1800.0) shouldBe (-0.1625 plusOrMinus 1E-6) + compute(1900.0) shouldBe (0.48750000000000004 plusOrMinus 1E-6) + compute(2000.0) shouldBe (1.1375 plusOrMinus 1E-6) + compute(2100.0) shouldBe (1.7875 plusOrMinus 1E-6) + compute(2200.0) shouldBe (2.4375 plusOrMinus 1E-6) + compute(2300.0) shouldBe (3.0875 plusOrMinus 1E-6) + compute(2400.0) shouldBe (3.7375000000000003 plusOrMinus 1E-6) + compute(2500.0) shouldBe (4.3875 plusOrMinus 1E-6) + compute(2600.0) shouldBe (5.0375000000000005 plusOrMinus 1E-6) + compute(2700.0) shouldBe (5.6875 plusOrMinus 1E-6) + compute(2800.0) shouldBe (6.3375 plusOrMinus 1E-6) + compute(2900.0) shouldBe (6.9875 plusOrMinus 1E-6) + compute(3000.0) shouldBe (7.6375 plusOrMinus 1E-6) } } } diff --git a/nebulosa-time/src/test/kotlin/S15Test.kt b/nebulosa-time/src/test/kotlin/S15Test.kt index 6d7cf2034..5e125ffc7 100644 --- a/nebulosa-time/src/test/kotlin/S15Test.kt +++ b/nebulosa-time/src/test/kotlin/S15Test.kt @@ -1,79 +1,80 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.time.S15 import nebulosa.time.Spline +import org.junit.jupiter.api.Test -class S15Test : StringSpec(), Spline by S15 { +class S15Test : Spline by S15 { - init { - "curve" { - compute(0.0) shouldBe (10441.312576 plusOrMinus 1E-8) - compute(100.0) shouldBe (9405.044447999999 plusOrMinus 1E-8) - compute(200.0) shouldBe (8424.698832 plusOrMinus 1E-8) - compute(300.0) shouldBe (7476.110944 plusOrMinus 1E-8) - compute(400.0) shouldBe (6535.116 plusOrMinus 1E-8) - compute(500.0) shouldBe (5586.600523148149 plusOrMinus 1E-8) - compute(600.0) shouldBe (4651.654629629629 plusOrMinus 1E-8) - compute(700.0) shouldBe (3760.419625 plusOrMinus 1E-8) - compute(800.0) shouldBe (2943.036814814815 plusOrMinus 1E-8) - compute(900.0) shouldBe (2229.647504629629 plusOrMinus 1E-8) - compute(1000.0) shouldBe (1650.393 plusOrMinus 1E-8) - compute(1100.0) shouldBe (1222.8812962962963 plusOrMinus 1E-8) - compute(1200.0) shouldBe (914.6107037037036 plusOrMinus 1E-8) - compute(1300.0) shouldBe (681.149 plusOrMinus 1E-8) - compute(1400.0) shouldBe (482.288 plusOrMinus 1E-8) - compute(1500.0) shouldBe (292.343 plusOrMinus 1E-8) - compute(1600.0) shouldBe (109.127 plusOrMinus 1E-8) - compute(1700.0) shouldBe (14.099507288629734 plusOrMinus 1E-8) - compute(1800.0) shouldBe (18.367 plusOrMinus 1E-8) - compute(1900.0) shouldBe (-1.977 plusOrMinus 1E-8) - compute(2000.0) shouldBe (63.808962962962966 plusOrMinus 1E-8) - compute(2100.0) shouldBe (-2952.9510000000005 plusOrMinus 1E-8) - compute(2200.0) shouldBe (-31950.310259259262 plusOrMinus 1E-8) - compute(2300.0) shouldBe (-117798.78062962965 plusOrMinus 1E-8) - compute(2400.0) shouldBe (-291387.25100000005 plusOrMinus 1E-8) - compute(2500.0) shouldBe (-583604.6102592595 plusOrMinus 1E-8) - compute(2600.0) shouldBe (-1025339.7472962962 plusOrMinus 1E-8) - compute(2700.0) shouldBe (-1647481.5510000002 plusOrMinus 1E-8) - compute(2800.0) shouldBe (-2480918.9102592585 plusOrMinus 1E-8) - compute(2900.0) shouldBe (-3556540.7139629633 plusOrMinus 1E-8) - compute(3000.0) shouldBe (-4905235.851000001 plusOrMinus 1E-8) - } - "derivative" { - with(derivative) { - compute(0.0) shouldBe (-10.72284312 plusOrMinus 1E-8) - compute(100.0) shouldBe (-10.04279408 plusOrMinus 1E-8) - compute(200.0) shouldBe (-9.60439288 plusOrMinus 1E-8) - compute(300.0) shouldBe (-9.40763952 plusOrMinus 1E-8) - compute(400.0) shouldBe (-9.452531666666667 plusOrMinus 1E-8) - compute(500.0) shouldBe (-9.467542361111112 plusOrMinus 1E-8) - compute(600.0) shouldBe (-9.181140000000001 plusOrMinus 1E-8) - compute(700.0) shouldBe (-8.593324583333334 plusOrMinus 1E-8) - compute(800.0) shouldBe (-7.704096111111111 plusOrMinus 1E-8) - compute(900.0) shouldBe (-6.513454583333333 plusOrMinus 1E-8) - compute(1000.0) shouldBe (-5.0214 plusOrMinus 1E-8) - compute(1100.0) shouldBe (-3.6039333333333334 plusOrMinus 1E-8) - compute(1200.0) shouldBe (-2.635517777777778 plusOrMinus 1E-8) - compute(1300.0) shouldBe (-2.106725 plusOrMinus 1E-8) - compute(1400.0) shouldBe (-1.9072624999999999 plusOrMinus 1E-8) - compute(1500.0) shouldBe (-1.9284100000000002 plusOrMinus 1E-8) - compute(1600.0) shouldBe (-1.5739400000000001 plusOrMinus 1E-8) - compute(1700.0) shouldBe (-0.23690262390670547 plusOrMinus 1E-8) - compute(1800.0) shouldBe (-0.34809999999999997 plusOrMinus 1E-8) - compute(1900.0) shouldBe (1.143 plusOrMinus 1E-8) - compute(2000.0) shouldBe (0.32577777777777783 plusOrMinus 1E-8) - compute(2100.0) shouldBe (-108.681 plusOrMinus 1E-8) - compute(2200.0) shouldBe (-522.7476666666668 plusOrMinus 1E-8) - compute(2300.0) shouldBe (-1245.7032222222224 plusOrMinus 1E-8) - compute(2400.0) shouldBe (-2277.547666666667 plusOrMinus 1E-8) - compute(2500.0) shouldBe (-3618.281000000001 plusOrMinus 1E-8) - compute(2600.0) shouldBe (-5267.903222222221 plusOrMinus 1E-8) - compute(2700.0) shouldBe (-7226.414333333333 plusOrMinus 1E-8) - compute(2800.0) shouldBe (-9493.814333333332 plusOrMinus 1E-8) - compute(2900.0) shouldBe (-12070.103222222226 plusOrMinus 1E-8) - compute(3000.0) shouldBe (-14955.281000000003 plusOrMinus 1E-8) - } + @Test + fun curve() { + compute(0.0) shouldBe (10441.312576 plusOrMinus 1E-8) + compute(100.0) shouldBe (9405.044447999999 plusOrMinus 1E-8) + compute(200.0) shouldBe (8424.698832 plusOrMinus 1E-8) + compute(300.0) shouldBe (7476.110944 plusOrMinus 1E-8) + compute(400.0) shouldBe (6535.116 plusOrMinus 1E-8) + compute(500.0) shouldBe (5586.600523148149 plusOrMinus 1E-8) + compute(600.0) shouldBe (4651.654629629629 plusOrMinus 1E-8) + compute(700.0) shouldBe (3760.419625 plusOrMinus 1E-8) + compute(800.0) shouldBe (2943.036814814815 plusOrMinus 1E-8) + compute(900.0) shouldBe (2229.647504629629 plusOrMinus 1E-8) + compute(1000.0) shouldBe (1650.393 plusOrMinus 1E-8) + compute(1100.0) shouldBe (1222.8812962962963 plusOrMinus 1E-8) + compute(1200.0) shouldBe (914.6107037037036 plusOrMinus 1E-8) + compute(1300.0) shouldBe (681.149 plusOrMinus 1E-8) + compute(1400.0) shouldBe (482.288 plusOrMinus 1E-8) + compute(1500.0) shouldBe (292.343 plusOrMinus 1E-8) + compute(1600.0) shouldBe (109.127 plusOrMinus 1E-8) + compute(1700.0) shouldBe (14.099507288629734 plusOrMinus 1E-8) + compute(1800.0) shouldBe (18.367 plusOrMinus 1E-8) + compute(1900.0) shouldBe (-1.977 plusOrMinus 1E-8) + compute(2000.0) shouldBe (63.808962962962966 plusOrMinus 1E-8) + compute(2100.0) shouldBe (-2952.9510000000005 plusOrMinus 1E-8) + compute(2200.0) shouldBe (-31950.310259259262 plusOrMinus 1E-8) + compute(2300.0) shouldBe (-117798.78062962965 plusOrMinus 1E-8) + compute(2400.0) shouldBe (-291387.25100000005 plusOrMinus 1E-8) + compute(2500.0) shouldBe (-583604.6102592595 plusOrMinus 1E-8) + compute(2600.0) shouldBe (-1025339.7472962962 plusOrMinus 1E-8) + compute(2700.0) shouldBe (-1647481.5510000002 plusOrMinus 1E-8) + compute(2800.0) shouldBe (-2480918.9102592585 plusOrMinus 1E-8) + compute(2900.0) shouldBe (-3556540.7139629633 plusOrMinus 1E-8) + compute(3000.0) shouldBe (-4905235.851000001 plusOrMinus 1E-8) + } + + @Test + fun derivative() { + with(derivative) { + compute(0.0) shouldBe (-10.72284312 plusOrMinus 1E-8) + compute(100.0) shouldBe (-10.04279408 plusOrMinus 1E-8) + compute(200.0) shouldBe (-9.60439288 plusOrMinus 1E-8) + compute(300.0) shouldBe (-9.40763952 plusOrMinus 1E-8) + compute(400.0) shouldBe (-9.452531666666667 plusOrMinus 1E-8) + compute(500.0) shouldBe (-9.467542361111112 plusOrMinus 1E-8) + compute(600.0) shouldBe (-9.181140000000001 plusOrMinus 1E-8) + compute(700.0) shouldBe (-8.593324583333334 plusOrMinus 1E-8) + compute(800.0) shouldBe (-7.704096111111111 plusOrMinus 1E-8) + compute(900.0) shouldBe (-6.513454583333333 plusOrMinus 1E-8) + compute(1000.0) shouldBe (-5.0214 plusOrMinus 1E-8) + compute(1100.0) shouldBe (-3.6039333333333334 plusOrMinus 1E-8) + compute(1200.0) shouldBe (-2.635517777777778 plusOrMinus 1E-8) + compute(1300.0) shouldBe (-2.106725 plusOrMinus 1E-8) + compute(1400.0) shouldBe (-1.9072624999999999 plusOrMinus 1E-8) + compute(1500.0) shouldBe (-1.9284100000000002 plusOrMinus 1E-8) + compute(1600.0) shouldBe (-1.5739400000000001 plusOrMinus 1E-8) + compute(1700.0) shouldBe (-0.23690262390670547 plusOrMinus 1E-8) + compute(1800.0) shouldBe (-0.34809999999999997 plusOrMinus 1E-8) + compute(1900.0) shouldBe (1.143 plusOrMinus 1E-8) + compute(2000.0) shouldBe (0.32577777777777783 plusOrMinus 1E-8) + compute(2100.0) shouldBe (-108.681 plusOrMinus 1E-8) + compute(2200.0) shouldBe (-522.7476666666668 plusOrMinus 1E-8) + compute(2300.0) shouldBe (-1245.7032222222224 plusOrMinus 1E-8) + compute(2400.0) shouldBe (-2277.547666666667 plusOrMinus 1E-8) + compute(2500.0) shouldBe (-3618.281000000001 plusOrMinus 1E-8) + compute(2600.0) shouldBe (-5267.903222222221 plusOrMinus 1E-8) + compute(2700.0) shouldBe (-7226.414333333333 plusOrMinus 1E-8) + compute(2800.0) shouldBe (-9493.814333333332 plusOrMinus 1E-8) + compute(2900.0) shouldBe (-12070.103222222226 plusOrMinus 1E-8) + compute(3000.0) shouldBe (-14955.281000000003 plusOrMinus 1E-8) } } } diff --git a/nebulosa-time/src/test/kotlin/TAIMinusUTCTest.kt b/nebulosa-time/src/test/kotlin/TAIMinusUTCTest.kt index 28cbe7cc3..c98793722 100644 --- a/nebulosa-time/src/test/kotlin/TAIMinusUTCTest.kt +++ b/nebulosa-time/src/test/kotlin/TAIMinusUTCTest.kt @@ -1,17 +1,16 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.shouldBeExactly import nebulosa.time.TAIMinusUTC import nebulosa.time.TimeDelta import nebulosa.time.TimeYMDHMS +import org.junit.jupiter.api.Test -class TAIMinusUTCTest : StringSpec(), TimeDelta by TAIMinusUTC { +class TAIMinusUTCTest : TimeDelta by TAIMinusUTC { - init { - "delta" { - delta(TimeYMDHMS(2003, 6, 1)) shouldBeExactly 32.0 - delta(TimeYMDHMS(2008, 1, 17)) shouldBeExactly 33.0 - delta(TimeYMDHMS(2017, 9, 1)) shouldBeExactly 37.0 - delta(TimeYMDHMS(2026, 1, 1)) shouldBeExactly 37.0 - } + @Test + fun delta() { + delta(TimeYMDHMS(2003, 6, 1)) shouldBeExactly 32.0 + delta(TimeYMDHMS(2008, 1, 17)) shouldBeExactly 33.0 + delta(TimeYMDHMS(2017, 9, 1)) shouldBeExactly 37.0 + delta(TimeYMDHMS(2026, 1, 1)) shouldBeExactly 37.0 } } diff --git a/nebulosa-time/src/test/kotlin/TDBMinusTTByFairheadAndBretagnon1990Test.kt b/nebulosa-time/src/test/kotlin/TDBMinusTTByFairheadAndBretagnon1990Test.kt index 213306b9f..749b3ff75 100644 --- a/nebulosa-time/src/test/kotlin/TDBMinusTTByFairheadAndBretagnon1990Test.kt +++ b/nebulosa-time/src/test/kotlin/TDBMinusTTByFairheadAndBretagnon1990Test.kt @@ -1,18 +1,17 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.time.TDB import nebulosa.time.TDBMinusTTByFairheadAndBretagnon1990 import nebulosa.time.TimeDelta +import org.junit.jupiter.api.Test -class TDBMinusTTByFairheadAndBretagnon1990Test : StringSpec(), TimeDelta by TDBMinusTTByFairheadAndBretagnon1990 { +class TDBMinusTTByFairheadAndBretagnon1990Test : TimeDelta by TDBMinusTTByFairheadAndBretagnon1990 { - init { - "delta" { - delta(TDB(2440423.345833333)) shouldBe (-0.00046798717637519603 plusOrMinus 1e-16) - delta(TDB(2448031.5)) shouldBe (0.0011585185926349208 plusOrMinus 1e-16) - delta(TDB(2451545.0)) shouldBe (-9.575743486095212e-05 plusOrMinus 1e-16) - delta(TDB(2456164.5)) shouldBe (-0.001241030165936046 plusOrMinus 1e-16) - } + @Test + fun delta() { + delta(TDB(2440423.345833333)) shouldBe (-0.00046798717637519603 plusOrMinus 1e-16) + delta(TDB(2448031.5)) shouldBe (0.0011585185926349208 plusOrMinus 1e-16) + delta(TDB(2451545.0)) shouldBe (-9.575743486095212e-05 plusOrMinus 1e-16) + delta(TDB(2456164.5)) shouldBe (-0.001241030165936046 plusOrMinus 1e-16) } } diff --git a/nebulosa-time/src/test/kotlin/TimeTest.kt b/nebulosa-time/src/test/kotlin/TimeTest.kt index 7c35d70cd..c654c5d15 100644 --- a/nebulosa-time/src/test/kotlin/TimeTest.kt +++ b/nebulosa-time/src/test/kotlin/TimeTest.kt @@ -1,960 +1,1034 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.doubles.shouldBeExactly import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe +import nebulosa.test.concat +import nebulosa.test.dataDirectory import nebulosa.time.* -import java.nio.file.Path +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test import java.time.LocalDateTime import java.time.ZoneOffset import kotlin.io.path.inputStream -class TimeTest : StringSpec() { - - init { - val iersa = IERSA() - iersa.load(Path.of("../data/finals2000A.all").inputStream()) - - val iersb = IERSB() - iersb.load(Path.of("../data/eopc04.1962-now.txt").inputStream()) - - IERS.attach(IERSAB(iersa, iersb)) - - "convert jd to datetime" { - TimeJD(2459902.1234).asDateTime().toString() shouldBe "2022-11-18T14:57:41.759993433" - TimeJD(2459902.6789).asDateTime().toString() shouldBe "2022-11-19T04:17:36.960014998" - TimeJD(2299161.0).asDateTime().toString() shouldBe "1582-10-15T12:00" - TimeJD(2299160.0).asDateTime(JulianCalendarCutOff.GREGORIAN_START).toString() shouldBe "1582-10-04T12:00" - } - "jd as year, month, day and fraction" { - val (yearMonthDay, fraction) = TimeJD(2400000.5, 50123.9999).asYearMonthDayAndFraction() - val (year, month, day) = yearMonthDay - year shouldBeExactly 1996 - month shouldBeExactly 2 - day shouldBeExactly 10 - fraction[0] shouldBe (0.9999 plusOrMinus 1e-9) - } - "time unix & time ymdhms" { - val now = LocalDateTime.now(ZoneOffset.UTC).withNano(0) - val unix = TimeUnix(now.toEpochSecond(ZoneOffset.UTC).toDouble()).value - val ymdhms = TimeYMDHMS(now).value - ymdhms shouldBeExactly unix - } - "high precision jd" { - val jd = TimeJD(2444495.5, 0.4788310185185185) - jd.whole shouldBe (2444496.0 plusOrMinus 1E-16) - jd.fraction shouldBe (-0.021168981481481497 plusOrMinus 1E-16) - } - "jd" { - val jd = TimeJD(2444495.9788310183) - jd.whole shouldBe (2444496.0 plusOrMinus 1E-9) - jd.fraction shouldBe (-0.021168981 plusOrMinus 1E-9) - } - "ymdhms" { - val jd = TimeYMDHMS(1980, 9, 12, 23, 29, 31.0) - jd.whole shouldBe (2444495.0 plusOrMinus 1E-16) - jd.fraction shouldBe (0.4788310185185185 plusOrMinus 1E-16) - } - "mjd" { - val mdj = TimeMJD(50001.47883101852) - mdj.whole shouldBe (2450002.0 plusOrMinus 1e-8) - mdj.fraction shouldBe (-0.021168981482333038 plusOrMinus 1e-12) - } - "utc:2022-01-01 12:00:00" { - val time = UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-1.2782876157107722e-06 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0004282407407407707 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0008007407407407707 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0008121958147759566 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0008007396238607902 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.001055591574221432 plusOrMinus 1E-14) - } - } - "utc:2023-06-01 23:59:59" { - val time = UTC(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.499987890906273 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49958333333333327 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49921083333333327 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49919901829547053 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49921082275980533 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49894796237489814 plusOrMinus 1E-14) - } - } - "utc:2024-01-01 00:00:00" { - val time = UTC(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49999989822685187 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49957175925925923 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49919925925925923 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49918729577550847 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4991992606390423 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4989330976467987 plusOrMinus 1E-14) - } - } - "utc:2025-12-31 17:59:43" { - val time = UTC(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980371005787044 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25023148148148155 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25060398148148155 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2506164542459721 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2506039804506639 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25088147386323706 plusOrMinus 1E-14) - } - } - "ut1:2022-01-01 12:00:00" { - val time = UT1(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (1.2782876138691737e-06 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0004295190283546413 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0008020190283546413 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.000813474102390718 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0008020179114750974 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0010568698618555591 plusOrMinus 1E-14) - } - } - "ut1:2023-06-01 23:59:59" { - val time = UT1(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998896094558004 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49958279831367913 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49921029831367913 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49919848327581595 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4992102877401513 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4989474273552358 plusOrMinus 1E-14) - } - } - "ut1:2024-01-01 00:00:00" { - val time = UT1(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4999998982268517 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4995718610324076 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4991993610324076 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4991873975486568 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49919936241219065 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49893319941994874 plusOrMinus 1E-14) - } - } - "ut1:2025-12-31 17:59:43" { - val time = UT1(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980277142361115 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25023101216435195 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25060351216435195 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2506159849288422 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25060351113353413 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2508810045461 plusOrMinus 1E-14) - } - } - "tai:2022-01-01 12:00:00" { - val time = TAI(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00042824074074071516 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00042951902898347746 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.00037249999999999995 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.00038395507373673244 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.00037249888297375804 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0006273508266944423 plusOrMinus 1E-14) - } - } - "tai:2023-06-01 23:59:59" { - val time = TAI(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4995601851851852 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49955965016637977 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49963907407407404 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49962725903650973 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49963906350042875 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49937620312216147 plusOrMinus 1E-14) - } - } - "tai:2024-01-01 00:00:00" { - val time = TAI(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49957175925925923 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4995718610330666 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4996275 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49961553651654766 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4996275013799246 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49936133839432106 plusOrMinus 1E-14) - } - } - "tai:2025-12-31 17:59:43" { - val time = TAI(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.249375 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24937546931712967 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2501757407407408 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2501882135049329 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2501757397097831 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.25045323311571627 plusOrMinus 1E-14) - } - } - "tt:2022-01-01 12:00:00" { - val time = TT(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0008007407407407215 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0008020190295288602 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00037249999999999995 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (1.1455073477126416e-05 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-1.1171534654999348e-09 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0002548508207915326 plusOrMinus 1E-14) - } - } - "tt:2023-06-01 23:59:59" { - val time = TT(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4991876851851852 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.499187150167117 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49961592592592596 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4999997590367693 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998843649967323 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49974870312783515 plusOrMinus 1E-14) - } - } - "tt:2024-01-01 00:00:00" { - val time = TT(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49919925925925923 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49919936103364004 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4996275 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49998803651680723 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4999999986199522 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4997338384002199 plusOrMinus 1E-14) - } - } - "tt:2025-12-31 17:59:43" { - val time = TT(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24900250000000002 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24900296931712967 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2494307407407408 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2498157135046733 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2498032397096612 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2500807331098187 plusOrMinus 1E-14) - } - } - "tcb:2022-01-01 12:00:00" { - val time = TCB(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.001055591557493719 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0010568698466550197 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0006273508167529781 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00025485081675297817 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00024339574345346468 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00025485193399348575 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - } - "tcb:2023-06-01 23:59:59" { - val time = TCB(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4989248142429501 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49892427922540217 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49935305498369087 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49972555498369087 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4997373700208124 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4997255655575102 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - } - "tcb:2024-01-01 00:00:00" { - val time = TCB(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.498933097663694 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4989331994384845 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49936133840443475 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49973383840443475 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.499745801887442 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49973383702429897 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - } - "tcb:2025-12-31 17:59:43" { - val time = TCB(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24872500763531546 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24872547695244512 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2491532483760562 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2495257483760562 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24953822113979535 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24952574734488586 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - } - "tcg:2022-01-01 12:00:00" { - val time = TCG(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0008121958142098429 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.000813474103014745 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.000383955073469143 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-1.1455073469143043e-05 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-1.1456190626520906e-05 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.00024339574714086394 plusOrMinus 1E-14) - } - } - "tcg:2023-06-01 23:59:59" { - val time = TCG(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49917587014788867 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49917533512984386 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49960411088862944 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49997661088862944 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49997662146238 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49976051816531164 plusOrMinus 1E-14) - } - } - "tcg:2024-01-01 00:00:00" { - val time = TCG(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4991872957760748 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49918739755047403 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49961553651681556 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998803651681556 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4999880351367638 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49974580188359374 plusOrMinus 1E-14) - } - } - "tcg:2025-12-31 17:59:43" { - val time = TCG(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24899002723607622 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24899049655320588 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24941826797681696 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24979076797681696 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2497907669457333 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2500682603456974 plusOrMinus 1E-14) - } - } - "tdb:2022-01-01 12:00:00" { - val time = TDB(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0008007396235872465 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.0008020179123753776 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (-0.00037249888284653445 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (1.1171534654999348e-09 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (1.1456190630592697e-05 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2459581.0 plusOrMinus 1E-14) - fraction shouldBe (0.0002548519379450154 plusOrMinus 1E-14) - } - } - "tdb:2023-06-01 23:59:59" { - val time = TDB(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) - - with(time.utc) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.499187674611438 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4991871395933698 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4996159153521787 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.4999884153521787 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4999997696105166 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460097.0 plusOrMinus 1E-14) - fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460098.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4997487137015826 plusOrMinus 1E-14) - } - } - "tdb:2024-01-01 00:00:00" { - val time = TDB(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) - - with(time.utc) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.499199260639307 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.4991993624136878 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.49962750138004774 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49999999861995226 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.4999880351367595 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2460310.0 plusOrMinus 1E-14) - fraction shouldBe (0.5 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2460311.0 plusOrMinus 1E-14) - fraction shouldBe (-0.49973383702017204 plusOrMinus 1E-14) - } - } - "tdb:2025-12-31 17:59:43" { - val time = TDB(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) - - with(time.utc) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24900250103107957 plusOrMinus 1E-14) - } - with(time.ut1) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24900297034820923 plusOrMinus 1E-14) - } - with(time.tai) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24943074177182037 plusOrMinus 1E-14) - } - with(time.tt) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324177182037 plusOrMinus 1E-14) - } - with(time.tcg) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24981571453575288 plusOrMinus 1E-14) - } - with(time.tdb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) - } - with(time.tcb) { - whole shouldBe (2461041.0 plusOrMinus 1E-14) - fraction shouldBe (0.2500807341408983 plusOrMinus 1E-14) - } +class TimeTest { + + @Test + fun convertJdToDatetime() { + TimeJD(2459902.1234).asDateTime().toString() shouldBe "2022-11-18T14:57:41.759993433" + TimeJD(2459902.6789).asDateTime().toString() shouldBe "2022-11-19T04:17:36.960014998" + TimeJD(2299161.0).asDateTime().toString() shouldBe "1582-10-15T12:00" + TimeJD(2299160.0).asDateTime(JulianCalendarCutOff.GREGORIAN_START).toString() shouldBe "1582-10-04T12:00" + } + + @Test + fun jdAsYearMonthDayAndFraction() { + val (yearMonthDay, fraction) = TimeJD(2400000.5, 50123.9999).asYearMonthDayAndFraction() + val (year, month, day) = yearMonthDay + year shouldBeExactly 1996 + month shouldBeExactly 2 + day shouldBeExactly 10 + fraction[0] shouldBe (0.9999 plusOrMinus 1e-9) + } + + @Test + fun timeUnixAndTimeYmdhms() { + val now = LocalDateTime.now(ZoneOffset.UTC).withNano(0) + val unix = TimeUnix(now.toEpochSecond(ZoneOffset.UTC).toDouble()).value + val ymdhms = TimeYMDHMS(now).value + ymdhms shouldBeExactly unix + } + + @Test + fun highPrecisionJd() { + val jd = TimeJD(2444495.5, 0.4788310185185185) + jd.whole shouldBe (2444496.0 plusOrMinus 1E-16) + jd.fraction shouldBe (-0.021168981481481497 plusOrMinus 1E-16) + } + + @Test + fun jd() { + val jd = TimeJD(2444495.9788310183) + jd.whole shouldBe (2444496.0 plusOrMinus 1E-9) + jd.fraction shouldBe (-0.021168981 plusOrMinus 1E-9) + } + + @Test + fun ymdhms() { + val jd = TimeYMDHMS(1980, 9, 12, 23, 29, 31.0) + jd.whole shouldBe (2444495.0 plusOrMinus 1E-16) + jd.fraction shouldBe (0.4788310185185185 plusOrMinus 1E-16) + } + + @Test + fun mjd() { + val mdj = TimeMJD(50001.47883101852) + mdj.whole shouldBe (2450002.0 plusOrMinus 1e-8) + mdj.fraction shouldBe (-0.021168981482333038 plusOrMinus 1e-12) + } + + @Test + fun utc20220101120000() { + val time = UTC(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-1.2782876157107722e-06 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0004282407407407707 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0008007407407407707 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0008121958147759566 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0008007396238607902 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.001055591574221432 plusOrMinus 1E-14) + } + } + + @Test + fun utc20230601235959() { + val time = UTC(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.499987890906273 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49958333333333327 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49921083333333327 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49919901829547053 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49921082275980533 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49894796237489814 plusOrMinus 1E-14) + } + } + + @Test + fun utc20240101000000() { + val time = UTC(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49999989822685187 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49957175925925923 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49919925925925923 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49918729577550847 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4991992606390423 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4989330976467987 plusOrMinus 1E-14) + } + } + + @Test + fun utc20251231175943() { + val time = UTC(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980371005787044 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25023148148148155 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25060398148148155 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2506164542459721 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2506039804506639 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25088147386323706 plusOrMinus 1E-14) + } + } + + @Test + fun ut120220101120000() { + val time = UT1(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (1.2782876138691737e-06 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0004295190283546413 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0008020190283546413 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.000813474102390718 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0008020179114750974 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0010568698618555591 plusOrMinus 1E-14) + } + } + + @Test + fun ut120230601235959() { + val time = UT1(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998896094558004 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49958279831367913 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49921029831367913 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49919848327581595 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4992102877401513 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4989474273552358 plusOrMinus 1E-14) + } + } + + @Test + fun ut120240101000000() { + val time = UT1(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4999998982268517 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4995718610324076 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4991993610324076 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4991873975486568 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49919936241219065 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49893319941994874 plusOrMinus 1E-14) + } + } + + @Test + fun ut120251231175943() { + val time = UT1(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980277142361115 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25023101216435195 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25060351216435195 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2506159849288422 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25060351113353413 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2508810045461 plusOrMinus 1E-14) + } + } + + @Test + fun tai20220101120000() { + val time = TAI(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00042824074074071516 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00042951902898347746 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.00037249999999999995 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.00038395507373673244 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.00037249888297375804 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0006273508266944423 plusOrMinus 1E-14) + } + } + + @Test + fun tai20230601235959() { + val time = TAI(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4995601851851852 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49955965016637977 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49963907407407404 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49962725903650973 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49963906350042875 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49937620312216147 plusOrMinus 1E-14) + } + } + + @Test + fun tai20240101000000() { + val time = TAI(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49957175925925923 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4995718610330666 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4996275 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49961553651654766 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4996275013799246 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49936133839432106 plusOrMinus 1E-14) + } + } + + @Test + fun tai20251231175943() { + val time = TAI(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.249375 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24937546931712967 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2501757407407408 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2501882135049329 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2501757397097831 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.25045323311571627 plusOrMinus 1E-14) + } + } + + @Test + fun tt20220101120000() { + val time = TT(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0008007407407407215 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0008020190295288602 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00037249999999999995 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (1.1455073477126416e-05 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-1.1171534654999348e-09 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0002548508207915326 plusOrMinus 1E-14) + } + } + + @Test + fun tt20230601235959() { + val time = TT(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4991876851851852 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.499187150167117 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49961592592592596 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4999997590367693 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998843649967323 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49974870312783515 plusOrMinus 1E-14) + } + } + + @Test + fun tt20240101000000() { + val time = TT(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49919925925925923 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49919936103364004 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4996275 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49998803651680723 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4999999986199522 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4997338384002199 plusOrMinus 1E-14) + } + } + + @Test + fun tt20251231175943() { + val time = TT(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24900250000000002 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24900296931712967 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2494307407407408 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2498157135046733 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2498032397096612 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2500807331098187 plusOrMinus 1E-14) + } + } + + @Test + fun tcb20220101120000() { + val time = TCB(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.001055591557493719 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0010568698466550197 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0006273508167529781 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00025485081675297817 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00024339574345346468 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00025485193399348575 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + } + + @Test + fun tcb20230601235959() { + val time = TCB(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4989248142429501 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49892427922540217 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49935305498369087 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49972555498369087 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4997373700208124 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4997255655575102 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + } + + @Test + fun tcb20240101000000() { + val time = TCB(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.498933097663694 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4989331994384845 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49936133840443475 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49973383840443475 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.499745801887442 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49973383702429897 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + } + + @Test + fun tcb20251231175943() { + val time = TCB(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24872500763531546 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24872547695244512 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2491532483760562 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2495257483760562 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24953822113979535 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24952574734488586 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + } + + @Test + fun tcg20220101120000() { + val time = TCG(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0008121958142098429 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.000813474103014745 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.000383955073469143 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-1.1455073469143043e-05 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-1.1456190626520906e-05 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.00024339574714086394 plusOrMinus 1E-14) + } + } + + @Test + fun tcg20230601235959() { + val time = TCG(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49917587014788867 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49917533512984386 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49960411088862944 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49997661088862944 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49997662146238 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49976051816531164 plusOrMinus 1E-14) + } + } + + @Test + fun tcg20240101000000() { + val time = TCG(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4991872957760748 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49918739755047403 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49961553651681556 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998803651681556 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4999880351367638 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49974580188359374 plusOrMinus 1E-14) + } + } + + @Test + fun tcg20251231175943() { + val time = TCG(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24899002723607622 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24899049655320588 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24941826797681696 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24979076797681696 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2497907669457333 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2500682603456974 plusOrMinus 1E-14) + } + } + + @Test + fun tdb20220101120000() { + val time = TDB(TimeYMDHMS(2022, 1, 1, 12, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0008007396235872465 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.0008020179123753776 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (-0.00037249888284653445 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (1.1171534654999348e-09 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (1.1456190630592697e-05 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2459581.0 plusOrMinus 1E-14) + fraction shouldBe (0.0002548519379450154 plusOrMinus 1E-14) + } + } + + @Test + fun tdb20230601235959() { + val time = TDB(TimeYMDHMS(2023, 6, 1, 23, 59, 59.0)) + + with(time.utc) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.499187674611438 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4991871395933698 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4996159153521787 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.4999884153521787 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4999997696105166 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460097.0 plusOrMinus 1E-14) + fraction shouldBe (0.49998842592592596 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460098.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4997487137015826 plusOrMinus 1E-14) + } + } + + @Test + fun tdb20240101000000() { + val time = TDB(TimeYMDHMS(2024, 1, 1, 0, 0, 0.0)) + + with(time.utc) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.499199260639307 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.4991993624136878 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.49962750138004774 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49999999861995226 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.4999880351367595 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2460310.0 plusOrMinus 1E-14) + fraction shouldBe (0.5 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2460311.0 plusOrMinus 1E-14) + fraction shouldBe (-0.49973383702017204 plusOrMinus 1E-14) + } + } + + @Test + fun tdb20251231175943() { + val time = TDB(TimeYMDHMS(2025, 12, 31, 17, 59, 43.0)) + + with(time.utc) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24900250103107957 plusOrMinus 1E-14) + } + with(time.ut1) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24900297034820923 plusOrMinus 1E-14) + } + with(time.tai) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24943074177182037 plusOrMinus 1E-14) + } + with(time.tt) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324177182037 plusOrMinus 1E-14) + } + with(time.tcg) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24981571453575288 plusOrMinus 1E-14) + } + with(time.tdb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.24980324074074078 plusOrMinus 1E-14) + } + with(time.tcb) { + whole shouldBe (2461041.0 plusOrMinus 1E-14) + fraction shouldBe (0.2500807341408983 plusOrMinus 1E-14) + } + } + + companion object { + + @JvmStatic + @BeforeAll + fun loadIERS() { + val iersa = IERSA() + val iersb = IERSB() + dataDirectory.concat("finals2000A.all").inputStream().use(iersa::load) + dataDirectory.concat("eopc04.1962-now.txt").inputStream().use(iersb::load) + IERS.attach(IERSAB(iersa, iersb)) } } } diff --git a/nebulosa-vizier/src/test/kotlin/VizierServiceTest.kt b/nebulosa-vizier/src/test/kotlin/VizierServiceTest.kt index c05846618..8010ce996 100644 --- a/nebulosa-vizier/src/test/kotlin/VizierServiceTest.kt +++ b/nebulosa-vizier/src/test/kotlin/VizierServiceTest.kt @@ -1,29 +1,32 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import nebulosa.vizier.VizierTAPService +import org.junit.jupiter.api.Test -class VizierServiceTest : StringSpec() { +class VizierServiceTest { - init { - "query" { - val vizier = VizierTAPService() - val query = """ + @Test + fun query() { + val query = """ SELECT TOP 100 sao.SAO, sao.HD, sao.Pmag, sao.Vmag, sao.SpType, sao.RA2000, sao.DE2000, sao.pmRA2000, sao.pmDE2000 FROM "I/131A/sao" AS sao ORDER BY SAO ASC """.trimIndent() - val data = vizier.query(query).execute().body().shouldNotBeNull() - data.size shouldBeExactly 100 + val data = SERVICE.query(query).execute().body().shouldNotBeNull() + data.size shouldBeExactly 100 - data[0].getField("SAO") shouldBe "1" - data[0].getField("HD") shouldBe "225019" - data[0].getField("RA2000") shouldBe "0.6735416666666666" + data[0].getField("SAO") shouldBe "1" + data[0].getField("HD") shouldBe "225019" + data[0].getField("RA2000") shouldBe "0.6735416666666666" - data[99].getField("SAO") shouldBe "100" - data[99].getField("HD").trim() shouldBe "" - data[99].getField("RA2000") shouldBe "9.303554166666665" - } + data[99].getField("SAO") shouldBe "100" + data[99].getField("HD").trim() shouldBe "" + data[99].getField("RA2000") shouldBe "9.303554166666665" + } + + companion object { + + @JvmStatic private val SERVICE = VizierTAPService() } } diff --git a/nebulosa-watney/src/test/kotlin/EquationsTest.kt b/nebulosa-watney/src/test/kotlin/EquationsTest.kt index 1efb1a557..27548bf50 100644 --- a/nebulosa-watney/src/test/kotlin/EquationsTest.kt +++ b/nebulosa-watney/src/test/kotlin/EquationsTest.kt @@ -1,24 +1,23 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.watney.platesolver.math.solveLeastSquares +import org.junit.jupiter.api.Test -class EquationsTest : StringSpec() { +class EquationsTest { - init { - "solve least squares" { - val equations = listOf( - doubleArrayOf(7.0, -6.0, 8.0, -15.0), - doubleArrayOf(3.0, 5.0, -2.0, -27.0), - doubleArrayOf(2.0, -2.0, 7.0, -20.0), - doubleArrayOf(4.0, 2.0, -5.0, -2.0), - doubleArrayOf(9.0, -8.0, 7.0, -5.0), - ) + @Test + fun solveLeastSquares() { + val equations = listOf( + doubleArrayOf(7.0, -6.0, 8.0, -15.0), + doubleArrayOf(3.0, 5.0, -2.0, -27.0), + doubleArrayOf(2.0, -2.0, 7.0, -20.0), + doubleArrayOf(4.0, 2.0, -5.0, -2.0), + doubleArrayOf(9.0, -8.0, 7.0, -5.0), + ) - val (a, b, c) = solveLeastSquares(equations) - a shouldBe (2.474 plusOrMinus 1e-3) - b shouldBe (5.397 plusOrMinus 1e-3) - c shouldBe (3.723 plusOrMinus 1e-3) - } + val (a, b, c) = solveLeastSquares(equations) + a shouldBe (2.474 plusOrMinus 1e-3) + b shouldBe (5.397 plusOrMinus 1e-3) + c shouldBe (3.723 plusOrMinus 1e-3) } } diff --git a/nebulosa-watney/src/test/kotlin/QuadDatabaseTest.kt b/nebulosa-watney/src/test/kotlin/QuadDatabaseTest.kt index 849b774f1..275c829fb 100644 --- a/nebulosa-watney/src/test/kotlin/QuadDatabaseTest.kt +++ b/nebulosa-watney/src/test/kotlin/QuadDatabaseTest.kt @@ -1,5 +1,3 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.longs.shouldBeExactly import io.kotest.matchers.nulls.shouldNotBeNull @@ -8,46 +6,51 @@ import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldEndWith import nebulosa.io.ByteOrder import nebulosa.math.deg -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.NonGitHubOnly +import nebulosa.test.concat +import nebulosa.test.homeDirectory import nebulosa.watney.platesolver.quad.CellStarQuad import nebulosa.watney.platesolver.quad.CompactQuadDatabase import nebulosa.watney.platesolver.quad.QuadDatabaseCellFileIndex -import java.nio.file.Path +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class QuadDatabaseTest : StringSpec() { +@NonGitHubOnly +class QuadDatabaseTest { - init { - val quadDir = Path.of(System.getProperty("user.home"), "Downloads", "watneyqdb") + @Test + fun cellIndexFile() { + val source = QUAD_DIR.concat("gaia2-00-07-20.qdbindex") + val index = QuadDatabaseCellFileIndex.read(source) - "cell index file" { - val source = Path.of("$quadDir", "gaia2-00-07-20.qdbindex") - val index = QuadDatabaseCellFileIndex.read(source) + index.byteOrder shouldBe ByteOrder.LITTLE + index.files shouldHaveSize 406 + index.files.forEach { "${it.descriptor.path}" shouldContain it.descriptor.id shouldEndWith ".qdb" } + val totalSizeInBytes = index.files.sumOf { (descriptor) -> descriptor.passes.sumOf { it.dataBlockByteLength } } + totalSizeInBytes shouldBeExactly 397360102 - (406 * 13) // 406 files, 13 bytes header + } + + @Test + fun cellQuads() { + val source = QUAD_DIR.concat("gaia2-00-07-20.qdbindex") + val index = QuadDatabaseCellFileIndex.read(source) + val cellFile = index.files.find { it.descriptor.id == "b10c01" }.shouldNotBeNull() + val quads = cellFile.quads(0.0, 0.0, 22.5.deg, 1, 4, 0, STAR_QUADS) + quads shouldHaveSize 1 + } - index.byteOrder shouldBe ByteOrder.LITTLE - index.files shouldHaveSize 406 - index.files.forEach { it.descriptor.path.toString().shouldContain(it.descriptor.id) shouldEndWith ".qdb" } - val totalSizeInBytes = index.files.sumOf { it.descriptor.passes.sumOf { it.dataBlockByteLength } } - totalSizeInBytes shouldBeExactly 397360102 - (406 * 13) // 406 files, 13 bytes header - } - "cell quads" { - val source = Path.of("$quadDir", "gaia2-00-07-20.qdbindex") - val index = QuadDatabaseCellFileIndex.read(source) - val cellFile = index.files.find { it.descriptor.id == "b10c01" }.shouldNotBeNull() - val quads = cellFile.quads(0.0, 0.0, 22.5.deg, 1, 4, 0, STAR_QUADS) - quads shouldHaveSize 1 - } - "find quads" { - val quadDatabase = CompactQuadDatabase(quadDir) - val quads = quadDatabase.quads(0.0, 0.0, 22.5.deg, 0, intArrayOf(-2, -1, 0, 1, 2), 4, 0, STAR_QUADS) - // StarQuad([0.46529815, 0.56207234, 0.56207234, 0.7299413, 0.95694715], 0.3420919, [11.586886405944824, -14.680888175964355]), - // StarQuad([0.44183773, 0.5151515, 0.542522, 0.56751466, 0.8023483], 0.3123361, [5.6454176902771, 10.346973419189453]), - println(quads) - } + @Test + fun findQuads() { + val quadDatabase = CompactQuadDatabase(QUAD_DIR) + val quads = quadDatabase.quads(0.0, 0.0, 22.5.deg, 0, intArrayOf(-2, -1, 0, 1, 2), 4, 0, STAR_QUADS) + // StarQuad([0.46529815, 0.56207234, 0.56207234, 0.7299413, 0.95694715], 0.3420919, [11.586886405944824, -14.680888175964355]), + // StarQuad([0.44183773, 0.5151515, 0.542522, 0.56751466, 0.8023483], 0.3123361, [5.6454176902771, 10.346973419189453]), + println(quads) } companion object { + @JvmStatic private val QUAD_DIR = homeDirectory.concat("Downloads", "watneyqdb") + @JvmStatic private val STAR_QUADS = listOf( CellStarQuad(doubleArrayOf(0.48065594, 0.5005079, 0.52168995, 0.59679097, 0.9971423), 259.138, 1933.375, 135.125), CellStarQuad(doubleArrayOf(0.1827525, 0.2504689, 0.40770558, 0.81601685, 0.97027695), 150.899, 1103.0, 59.0), diff --git a/nebulosa-watney/src/test/kotlin/SearchStrategyTest.kt b/nebulosa-watney/src/test/kotlin/SearchStrategyTest.kt index bc1753a19..61f32cd5c 100644 --- a/nebulosa-watney/src/test/kotlin/SearchStrategyTest.kt +++ b/nebulosa-watney/src/test/kotlin/SearchStrategyTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.maps.shouldContainKey import nebulosa.math.deg @@ -8,56 +7,58 @@ import nebulosa.watney.platesolver.BlindSearchStrategy import nebulosa.watney.platesolver.BlindSearchStrategyOptions import nebulosa.watney.platesolver.NearbySearchStrategy import nebulosa.watney.platesolver.NearbySearchStrategyOptions +import org.junit.jupiter.api.Test -class SearchStrategyTest : StringSpec() { - - init { - "blind" { - var div = 128 - var i = 0 - - val searchRunSizes = intArrayOf(898184, 226556, 57644, 14916, 3964, 1096, 308, 76) - val searchRunSizesByRadius = listOf( - // @formatter:off - listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952, 0.703125 to 42728, 0.3515625 to 168912, 0.17578125 to 671628), - listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952, 0.703125 to 42728, 0.3515625 to 168912), - listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952, 0.703125 to 42728), - listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952), - listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868), - listOf(22.5 to 76, 11.25 to 232, 5.625 to 788), - listOf(22.5 to 76, 11.25 to 232), - listOf(22.5 to 76), - // @formatter:on - ) - - while (div >= 1) { - val options = BlindSearchStrategyOptions(minRadius = (22.5 / div).deg) - val strategy = BlindSearchStrategy(options) - val searchRuns = strategy.searchQueue() - searchRuns shouldHaveSize searchRunSizes[i] - val groupedSearchRuns = searchRuns.groupBy { it.radius.toDegrees } - - searchRunSizesByRadius[i].forEach { - groupedSearchRuns shouldContainKey it.first - groupedSearchRuns[it.first]!! shouldHaveSize it.second - } - - div /= 2 - i++ +class SearchStrategyTest { + + @Test + fun blind() { + var div = 128 + var i = 0 + + val searchRunSizes = intArrayOf(898184, 226556, 57644, 14916, 3964, 1096, 308, 76) + val searchRunSizesByRadius = listOf( + // @formatter:off + listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952, 0.703125 to 42728, 0.3515625 to 168912, 0.17578125 to 671628), + listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952, 0.703125 to 42728, 0.3515625 to 168912), + listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952, 0.703125 to 42728), + listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868, 1.40625 to 10952), + listOf(22.5 to 76, 11.25 to 232, 5.625 to 788, 2.8125 to 2868), + listOf(22.5 to 76, 11.25 to 232, 5.625 to 788), + listOf(22.5 to 76, 11.25 to 232), + listOf(22.5 to 76), + // @formatter:on + ) + + while (div >= 1) { + val options = BlindSearchStrategyOptions(minRadius = (22.5 / div).deg) + val strategy = BlindSearchStrategy(options) + val searchRuns = strategy.searchQueue() + searchRuns shouldHaveSize searchRunSizes[i] + val groupedSearchRuns = searchRuns.groupBy { it.radius.toDegrees } + + searchRunSizesByRadius[i].forEach { + groupedSearchRuns shouldContainKey it.first + groupedSearchRuns[it.first]!! shouldHaveSize it.second } + + div /= 2 + i++ } - "nearby" { - val intermediateFieldRadiusSteps = intArrayOf(-1, -1, 0, 3) - val minFieldRadii = intArrayOf(0, 1, 1, 1) - val maxFieldRadii = intArrayOf(2, 8, 8, 8) - val searchRunsCount = intArrayOf(39, 211, 158, 193) - - repeat(intermediateFieldRadiusSteps.size) { - val options = NearbySearchStrategyOptions(10.deg, minFieldRadii[it].deg, maxFieldRadii[it].deg, intermediateFieldRadiusSteps[it]) - val strategy = NearbySearchStrategy("06 45 08.91728".hours, "-16 42 58.0171".deg, options) - val queue = strategy.searchQueue() - queue shouldHaveSize searchRunsCount[it] - } + } + + @Test + fun nearby() { + val intermediateFieldRadiusSteps = intArrayOf(-1, -1, 0, 3) + val minFieldRadii = intArrayOf(0, 1, 1, 1) + val maxFieldRadii = intArrayOf(2, 8, 8, 8) + val searchRunsCount = intArrayOf(39, 211, 158, 193) + + repeat(intermediateFieldRadiusSteps.size) { + val options = NearbySearchStrategyOptions(10.deg, minFieldRadii[it].deg, maxFieldRadii[it].deg, intermediateFieldRadiusSteps[it]) + val strategy = NearbySearchStrategy("06 45 08.91728".hours, "-16 42 58.0171".deg, options) + val queue = strategy.searchQueue() + queue shouldHaveSize searchRunsCount[it] } } } diff --git a/nebulosa-watney/src/test/kotlin/SkySegmentSphereTest.kt b/nebulosa-watney/src/test/kotlin/SkySegmentSphereTest.kt index 6119dce66..f866c8460 100644 --- a/nebulosa-watney/src/test/kotlin/SkySegmentSphereTest.kt +++ b/nebulosa-watney/src/test/kotlin/SkySegmentSphereTest.kt @@ -1,26 +1,27 @@ -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.math.toDegrees import nebulosa.watney.platesolver.quad.SkySegmentSphere +import org.junit.jupiter.api.Test -class SkySegmentSphereTest : StringSpec() { +class SkySegmentSphereTest { - init { - "size" { - SkySegmentSphere shouldHaveSize CELLS.size - } - "cell" { - for (cell in CELLS) { - with(SkySegmentSphere[cell[0].toInt(), cell[1].toInt()]) { - width.toDegrees shouldBe (cell[2] plusOrMinus 1e-4) - height.toDegrees shouldBe (cell[3] plusOrMinus 1e-4) - bounds.left.toDegrees shouldBe (cell[4] plusOrMinus 1e-4) - bounds.right.toDegrees shouldBe (cell[5] plusOrMinus 1e-4) - bounds.bottom.toDegrees shouldBe (cell[6] plusOrMinus 1e-4) - bounds.top.toDegrees shouldBe (cell[7] plusOrMinus 1e-4) - } + @Test + fun size() { + SkySegmentSphere shouldHaveSize CELLS.size + } + + @Test + fun cell() { + for (cell in CELLS) { + with(SkySegmentSphere[cell[0].toInt(), cell[1].toInt()]) { + width.toDegrees shouldBe (cell[2] plusOrMinus 1e-4) + height.toDegrees shouldBe (cell[3] plusOrMinus 1e-4) + bounds.left.toDegrees shouldBe (cell[4] plusOrMinus 1e-4) + bounds.right.toDegrees shouldBe (cell[5] plusOrMinus 1e-4) + bounds.bottom.toDegrees shouldBe (cell[6] plusOrMinus 1e-4) + bounds.top.toDegrees shouldBe (cell[7] plusOrMinus 1e-4) } } } diff --git a/nebulosa-watney/src/test/kotlin/WatnetPlateSolverTest.kt b/nebulosa-watney/src/test/kotlin/WatnetPlateSolverTest.kt index 91f5e0b5f..a13b581a5 100644 --- a/nebulosa-watney/src/test/kotlin/WatnetPlateSolverTest.kt +++ b/nebulosa-watney/src/test/kotlin/WatnetPlateSolverTest.kt @@ -1,4 +1,3 @@ -import io.kotest.core.annotation.EnabledIf import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.ints.shouldBeExactly @@ -6,148 +5,151 @@ import io.kotest.matchers.shouldBe import nebulosa.fits.fits import nebulosa.image.Image import nebulosa.math.deg -import nebulosa.test.AbstractFitsAndXisfTest -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.M31_FITS +import nebulosa.test.NonGitHubOnly +import nebulosa.test.concat +import nebulosa.test.homeDirectory import nebulosa.watney.platesolver.WatneyPlateSolver import nebulosa.watney.platesolver.quad.CompactQuadDatabase import nebulosa.watney.stardetector.Star -import java.nio.file.Path +import org.junit.jupiter.api.Test -@EnabledIf(NonGitHubOnlyCondition::class) -class WatnetPlateSolverTest : AbstractFitsAndXisfTest() { +@NonGitHubOnly +class WatnetPlateSolverTest { - init { - val quadDir = Path.of(System.getProperty("user.home"), "Downloads", "watneyqdb") - val quadDatabase = CompactQuadDatabase(quadDir) - val solver = WatneyPlateSolver(quadDatabase) + @Test + fun blind() { + val image = M31_FITS.fits().use(Image::open) + println(SOLVER.solve(null, image, 0.0, 0.0, 0.deg)) + } - "blind" { - val image = Image.open(closeAfterEach(M31_FITS.fits())) - println(solver.solve(null, image, 0.0, 0.0, 0.deg)) - } - "form image star quads" { - val (quads, countInFirstPass) = WatneyPlateSolver.formImageStarQuads(M31_STARS) - quads shouldHaveSize 42 - countInFirstPass shouldBeExactly 36 + @Test + fun formImageStarQuads() { + val (quads, countInFirstPass) = WatneyPlateSolver.formImageStarQuads(M31_STARS) + quads shouldHaveSize 42 + countInFirstPass shouldBeExactly 36 - // Original Quads generated by Watney C# implementation using the M31 image. + // Original Quads generated by Watney C# implementation using the M31 image. - val origQuadStars = listOf( - doubleArrayOf(358.5, 0.5, 399.5, 58.5, 371.5, 164.0, 487.5, 108.0), - doubleArrayOf(530.0, 3.0, 514.5, 110.0, 487.5, 108.0, 399.5, 58.5), - doubleArrayOf(957.0, 3.5, 971.5, 6.5, 1034.0, 192.0, 985.5, 240.5), - doubleArrayOf(514.5, 110.0, 487.5, 108.0, 566.5, 203.5, 530.0, 3.0), - doubleArrayOf(697.0, 111.5, 642.5, 187.0, 657.0, 224.5, 566.5, 203.5), - doubleArrayOf(371.5, 164.0, 399.5, 58.5, 487.5, 108.0, 514.5, 110.0), - doubleArrayOf(17.0, 169.5, 82.5, 180.5, 70.5, 283.5, 114.0, 284.0), - doubleArrayOf(642.5, 187.0, 657.0, 224.5, 599.5, 248.5, 566.5, 203.5), - doubleArrayOf(1034.0, 192.0, 985.5, 240.5, 1060.0, 287.0, 971.5, 6.5), - doubleArrayOf(566.5, 203.5, 599.5, 248.5, 583.5, 259.5, 642.5, 187.0), - doubleArrayOf(657.0, 224.5, 642.5, 187.0, 599.5, 248.5, 583.5, 259.5), - doubleArrayOf(985.5, 240.5, 1034.0, 192.0, 1060.0, 287.0, 1053.0, 426.0), - doubleArrayOf(599.5, 248.5, 583.5, 259.5, 566.5, 203.5, 657.0, 224.5), - doubleArrayOf(583.5, 259.5, 599.5, 248.5, 574.5, 309.5, 566.5, 203.5), - doubleArrayOf(70.5, 283.5, 114.0, 284.0, 126.5, 300.5, 133.0, 305.0), - doubleArrayOf(5.5, 321.0, 70.5, 283.5, 114.0, 284.0, 126.5, 300.5), - doubleArrayOf(403.5, 326.5, 474.5, 379.0, 271.0, 374.0, 372.5, 474.5), - doubleArrayOf(240.5, 370.0, 271.0, 374.0, 248.5, 462.0, 133.0, 305.0), - doubleArrayOf(271.0, 374.0, 240.5, 370.0, 248.5, 462.0, 403.5, 326.5), - doubleArrayOf(474.5, 379.0, 403.5, 326.5, 540.5, 460.0, 574.5, 309.5), - doubleArrayOf(738.5, 402.5, 686.5, 411.5, 616.5, 459.0, 828.0, 539.0), - doubleArrayOf(1258.5, 410.0, 1196.5, 573.0, 1208.0, 587.5, 1053.0, 426.0), - doubleArrayOf(686.5, 411.5, 738.5, 402.5, 616.5, 459.0, 574.5, 309.5), - doubleArrayOf(1053.0, 426.0, 1060.0, 287.0, 985.5, 240.5, 1196.5, 573.0), - doubleArrayOf(616.5, 459.0, 540.5, 460.0, 686.5, 411.5, 533.5, 488.0), - doubleArrayOf(540.5, 460.0, 533.5, 488.0, 616.5, 459.0, 474.5, 379.0), - doubleArrayOf(248.5, 462.0, 195.0, 503.0, 271.0, 374.0, 240.5, 370.0), - doubleArrayOf(372.5, 474.5, 365.5, 492.5, 248.5, 462.0, 474.5, 379.0), - doubleArrayOf(365.5, 492.5, 372.5, 474.5, 248.5, 462.0, 271.0, 374.0), - doubleArrayOf(828.0, 539.0, 738.5, 402.5, 680.5, 656.0, 686.5, 411.5), - doubleArrayOf(24.0, 563.0, 195.0, 503.0, 188.5, 655.5, 5.5, 321.0), - doubleArrayOf(1196.5, 573.0, 1208.0, 587.5, 1066.5, 642.5, 1258.5, 410.0), - doubleArrayOf(652.5, 637.5, 651.0, 655.0, 680.5, 656.0, 524.0, 691.0), - doubleArrayOf(1066.5, 642.5, 1196.5, 573.0, 1208.0, 587.5, 1053.0, 426.0), - doubleArrayOf(188.5, 655.5, 296.5, 658.0, 195.0, 503.0, 24.0, 563.0), - doubleArrayOf(296.5, 658.0, 188.5, 655.5, 365.5, 492.5, 195.0, 503.0), - doubleArrayOf(828.0, 539.0, 738.5, 402.5, 686.5, 411.5, 652.5, 637.5), - doubleArrayOf(652.5, 637.5, 651.0, 655.0, 616.5, 459.0, 533.5, 488.0), - doubleArrayOf(188.5, 655.5, 195.0, 503.0, 24.0, 563.0, 248.5, 462.0), - doubleArrayOf(24.0, 563.0, 195.0, 503.0, 5.5, 321.0, 248.5, 462.0), - doubleArrayOf(652.5, 637.5, 616.5, 459.0, 533.5, 488.0, 828.0, 539.0), - doubleArrayOf(1258.5, 410.0, 1196.5, 573.0, 1053.0, 426.0, 1060.0, 287.0), - ) + val origQuadStars = listOf( + doubleArrayOf(358.5, 0.5, 399.5, 58.5, 371.5, 164.0, 487.5, 108.0), + doubleArrayOf(530.0, 3.0, 514.5, 110.0, 487.5, 108.0, 399.5, 58.5), + doubleArrayOf(957.0, 3.5, 971.5, 6.5, 1034.0, 192.0, 985.5, 240.5), + doubleArrayOf(514.5, 110.0, 487.5, 108.0, 566.5, 203.5, 530.0, 3.0), + doubleArrayOf(697.0, 111.5, 642.5, 187.0, 657.0, 224.5, 566.5, 203.5), + doubleArrayOf(371.5, 164.0, 399.5, 58.5, 487.5, 108.0, 514.5, 110.0), + doubleArrayOf(17.0, 169.5, 82.5, 180.5, 70.5, 283.5, 114.0, 284.0), + doubleArrayOf(642.5, 187.0, 657.0, 224.5, 599.5, 248.5, 566.5, 203.5), + doubleArrayOf(1034.0, 192.0, 985.5, 240.5, 1060.0, 287.0, 971.5, 6.5), + doubleArrayOf(566.5, 203.5, 599.5, 248.5, 583.5, 259.5, 642.5, 187.0), + doubleArrayOf(657.0, 224.5, 642.5, 187.0, 599.5, 248.5, 583.5, 259.5), + doubleArrayOf(985.5, 240.5, 1034.0, 192.0, 1060.0, 287.0, 1053.0, 426.0), + doubleArrayOf(599.5, 248.5, 583.5, 259.5, 566.5, 203.5, 657.0, 224.5), + doubleArrayOf(583.5, 259.5, 599.5, 248.5, 574.5, 309.5, 566.5, 203.5), + doubleArrayOf(70.5, 283.5, 114.0, 284.0, 126.5, 300.5, 133.0, 305.0), + doubleArrayOf(5.5, 321.0, 70.5, 283.5, 114.0, 284.0, 126.5, 300.5), + doubleArrayOf(403.5, 326.5, 474.5, 379.0, 271.0, 374.0, 372.5, 474.5), + doubleArrayOf(240.5, 370.0, 271.0, 374.0, 248.5, 462.0, 133.0, 305.0), + doubleArrayOf(271.0, 374.0, 240.5, 370.0, 248.5, 462.0, 403.5, 326.5), + doubleArrayOf(474.5, 379.0, 403.5, 326.5, 540.5, 460.0, 574.5, 309.5), + doubleArrayOf(738.5, 402.5, 686.5, 411.5, 616.5, 459.0, 828.0, 539.0), + doubleArrayOf(1258.5, 410.0, 1196.5, 573.0, 1208.0, 587.5, 1053.0, 426.0), + doubleArrayOf(686.5, 411.5, 738.5, 402.5, 616.5, 459.0, 574.5, 309.5), + doubleArrayOf(1053.0, 426.0, 1060.0, 287.0, 985.5, 240.5, 1196.5, 573.0), + doubleArrayOf(616.5, 459.0, 540.5, 460.0, 686.5, 411.5, 533.5, 488.0), + doubleArrayOf(540.5, 460.0, 533.5, 488.0, 616.5, 459.0, 474.5, 379.0), + doubleArrayOf(248.5, 462.0, 195.0, 503.0, 271.0, 374.0, 240.5, 370.0), + doubleArrayOf(372.5, 474.5, 365.5, 492.5, 248.5, 462.0, 474.5, 379.0), + doubleArrayOf(365.5, 492.5, 372.5, 474.5, 248.5, 462.0, 271.0, 374.0), + doubleArrayOf(828.0, 539.0, 738.5, 402.5, 680.5, 656.0, 686.5, 411.5), + doubleArrayOf(24.0, 563.0, 195.0, 503.0, 188.5, 655.5, 5.5, 321.0), + doubleArrayOf(1196.5, 573.0, 1208.0, 587.5, 1066.5, 642.5, 1258.5, 410.0), + doubleArrayOf(652.5, 637.5, 651.0, 655.0, 680.5, 656.0, 524.0, 691.0), + doubleArrayOf(1066.5, 642.5, 1196.5, 573.0, 1208.0, 587.5, 1053.0, 426.0), + doubleArrayOf(188.5, 655.5, 296.5, 658.0, 195.0, 503.0, 24.0, 563.0), + doubleArrayOf(296.5, 658.0, 188.5, 655.5, 365.5, 492.5, 195.0, 503.0), + doubleArrayOf(828.0, 539.0, 738.5, 402.5, 686.5, 411.5, 652.5, 637.5), + doubleArrayOf(652.5, 637.5, 651.0, 655.0, 616.5, 459.0, 533.5, 488.0), + doubleArrayOf(188.5, 655.5, 195.0, 503.0, 24.0, 563.0, 248.5, 462.0), + doubleArrayOf(24.0, 563.0, 195.0, 503.0, 5.5, 321.0, 248.5, 462.0), + doubleArrayOf(652.5, 637.5, 616.5, 459.0, 533.5, 488.0, 828.0, 539.0), + doubleArrayOf(1258.5, 410.0, 1196.5, 573.0, 1053.0, 426.0, 1060.0, 287.0), + ) - val origQuads = listOf( - doubleArrayOf(0.4229872, 0.6012765, 0.6500249, 0.7670894, 0.9767487, 167.92, 404.25, 82.75), - doubleArrayOf(0.19091523, 0.7119774, 0.76239824, 0.7987724, 0.8885384, 141.81, 482.88, 69.88), - doubleArrayOf(0.06203029, 0.28733647, 0.82002467, 0.85301214, 0.9820323, 238.71, 987.0, 110.63), - doubleArrayOf(0.1328489, 0.52497375, 0.530517, 0.55582803, 0.6081619, 203.8, 524.63, 106.13), - doubleArrayOf(0.25180638, 0.48707265, 0.5818563, 0.58317775, 0.75074446, 159.67, 640.75, 181.63), - doubleArrayOf(0.17712061, 0.66053337, 0.7140859, 0.8243369, 0.84268737, 152.86, 443.25, 110.13), - doubleArrayOf(0.28989518, 0.44259232, 0.6910156, 0.7209406, 0.8391714, 150.06, 71.0, 229.38), - doubleArrayOf(0.43276387, 0.6006513, 0.67066383, 0.80772877, 0.8371013, 92.9, 616.38, 215.88), - doubleArrayOf(0.23319396, 0.29857823, 0.3348643, 0.66550833, 0.79698896, 294.13, 1012.75, 181.5), - doubleArrayOf(0.20772238, 0.59699667, 0.6260989, 0.8028142, 0.83200794, 93.47, 598.0, 224.63), - doubleArrayOf(0.20772238, 0.43013072, 0.6665832, 0.8028142, 0.8709221, 93.47, 620.63, 229.88), - doubleArrayOf(0.2921554, 0.37407166, 0.41953236, 0.59281886, 0.8408198, 234.77, 1033.13, 286.38), - doubleArrayOf(0.208994, 0.6006513, 0.6299317, 0.67066383, 0.8762536, 92.9, 601.63, 234.0), - doubleArrayOf(0.18265495, 0.47791952, 0.5249526, 0.5505428, 0.6201627, 106.3, 581.0, 255.25), - doubleArrayOf(0.11961175, 0.31319097, 0.42847058, 0.6581907, 0.8854501, 66.09, 111.0, 293.25), - doubleArrayOf(0.16867274, 0.35447648, 0.47686976, 0.6114654, 0.93408805, 122.72, 79.13, 297.25), - doubleArrayOf(0.43378574, 0.6864228, 0.69147134, 0.7016915, 0.74283123, 203.56, 380.38, 388.5), - doubleArrayOf(0.15782383, 0.46601853, 0.473798, 0.6445258, 0.79159623, 194.91, 223.25, 377.75), - doubleArrayOf(0.14941548, 0.44119054, 0.44855553, 0.6836948, 0.8194446, 205.88, 290.88, 383.13), - doubleArrayOf(0.46161732, 0.5462143, 0.6366278, 0.8065975, 0.8983449, 191.29, 498.25, 368.75), - doubleArrayOf(0.23338081, 0.37410653, 0.5945751, 0.7218385, 0.8423202, 226.12, 717.38, 453.0), - doubleArrayOf(0.082676105, 0.77907515, 0.8244223, 0.9177246, 0.92081827, 223.85, 1179.0, 499.13), - doubleArrayOf(0.2799132, 0.44869733, 0.71312374, 0.803495, 0.8236593, 188.53, 654.0, 395.63), - doubleArrayOf(0.22300959, 0.35341972, 0.50126994, 0.5216613, 0.80473685, 393.8, 1073.75, 381.63), - doubleArrayOf(0.16872369, 0.44432908, 0.4945342, 0.5139766, 0.8993664, 171.06, 594.25, 454.63), - doubleArrayOf(0.17708255, 0.4663419, 0.53943986, 0.64106923, 0.76046133, 162.98, 541.25, 446.5), - doubleArrayOf(0.20545381, 0.45018867, 0.60665923, 0.6167865, 0.9388506, 149.72, 238.75, 427.25), - doubleArrayOf(0.080217935, 0.50220335, 0.5176477, 0.58036906, 0.6536133, 240.76, 365.25, 452.0), - doubleArrayOf(0.12742372, 0.5992795, 0.79773456, 0.82226735, 0.942405, 151.57, 314.38, 450.75), - doubleArrayOf(0.20293406, 0.6276678, 0.7239713, 0.7324315, 0.9404851, 260.05, 733.38, 502.25), - doubleArrayOf(0.40032506, 0.47528812, 0.4949649, 0.63654554, 0.6890982, 381.29, 103.25, 510.63), - doubleArrayOf(0.0613762, 0.48887974, 0.50347656, 0.57836145, 0.61202574, 301.53, 1182.38, 553.25), - doubleArrayOf(0.10952552, 0.18405987, 0.20926912, 0.8231408, 0.86796653, 160.37, 627.0, 659.88), - doubleArrayOf(0.082676105, 0.6585399, 0.67820233, 0.9177246, 0.96905917, 223.85, 1131.0, 557.25), - doubleArrayOf(0.3743402, 0.5289205, 0.6279637, 0.64201605, 0.65396124, 288.58, 176.0, 594.88), - doubleArrayOf(0.44896066, 0.63435477, 0.70992845, 0.7451902, 0.7699947, 240.62, 261.38, 577.25), - doubleArrayOf(0.21088836, 0.65227014, 0.7611402, 0.8042312, 0.9132892, 250.24, 726.38, 497.63), - doubleArrayOf(0.086017, 0.43057266, 0.8917693, 0.935772, 0.9746273, 204.19, 613.38, 559.88), - doubleArrayOf(0.27380574, 0.62004495, 0.7361518, 0.76662827, 0.82295257, 246.17, 164.0, 545.88), - doubleArrayOf(0.23991768, 0.64504075, 0.8638924, 0.8762333, 0.9352145, 280.94, 118.25, 462.25), - doubleArrayOf(0.294163, 0.609248, 0.63931024, 0.6733474, 0.75656414, 298.88, 657.63, 530.88), - doubleArrayOf(0.43917423, 0.55030274, 0.64823836, 0.6504235, 0.7368766, 316.9, 1142.0, 424.0), - ) + val origQuads = listOf( + doubleArrayOf(0.4229872, 0.6012765, 0.6500249, 0.7670894, 0.9767487, 167.92, 404.25, 82.75), + doubleArrayOf(0.19091523, 0.7119774, 0.76239824, 0.7987724, 0.8885384, 141.81, 482.88, 69.88), + doubleArrayOf(0.06203029, 0.28733647, 0.82002467, 0.85301214, 0.9820323, 238.71, 987.0, 110.63), + doubleArrayOf(0.1328489, 0.52497375, 0.530517, 0.55582803, 0.6081619, 203.8, 524.63, 106.13), + doubleArrayOf(0.25180638, 0.48707265, 0.5818563, 0.58317775, 0.75074446, 159.67, 640.75, 181.63), + doubleArrayOf(0.17712061, 0.66053337, 0.7140859, 0.8243369, 0.84268737, 152.86, 443.25, 110.13), + doubleArrayOf(0.28989518, 0.44259232, 0.6910156, 0.7209406, 0.8391714, 150.06, 71.0, 229.38), + doubleArrayOf(0.43276387, 0.6006513, 0.67066383, 0.80772877, 0.8371013, 92.9, 616.38, 215.88), + doubleArrayOf(0.23319396, 0.29857823, 0.3348643, 0.66550833, 0.79698896, 294.13, 1012.75, 181.5), + doubleArrayOf(0.20772238, 0.59699667, 0.6260989, 0.8028142, 0.83200794, 93.47, 598.0, 224.63), + doubleArrayOf(0.20772238, 0.43013072, 0.6665832, 0.8028142, 0.8709221, 93.47, 620.63, 229.88), + doubleArrayOf(0.2921554, 0.37407166, 0.41953236, 0.59281886, 0.8408198, 234.77, 1033.13, 286.38), + doubleArrayOf(0.208994, 0.6006513, 0.6299317, 0.67066383, 0.8762536, 92.9, 601.63, 234.0), + doubleArrayOf(0.18265495, 0.47791952, 0.5249526, 0.5505428, 0.6201627, 106.3, 581.0, 255.25), + doubleArrayOf(0.11961175, 0.31319097, 0.42847058, 0.6581907, 0.8854501, 66.09, 111.0, 293.25), + doubleArrayOf(0.16867274, 0.35447648, 0.47686976, 0.6114654, 0.93408805, 122.72, 79.13, 297.25), + doubleArrayOf(0.43378574, 0.6864228, 0.69147134, 0.7016915, 0.74283123, 203.56, 380.38, 388.5), + doubleArrayOf(0.15782383, 0.46601853, 0.473798, 0.6445258, 0.79159623, 194.91, 223.25, 377.75), + doubleArrayOf(0.14941548, 0.44119054, 0.44855553, 0.6836948, 0.8194446, 205.88, 290.88, 383.13), + doubleArrayOf(0.46161732, 0.5462143, 0.6366278, 0.8065975, 0.8983449, 191.29, 498.25, 368.75), + doubleArrayOf(0.23338081, 0.37410653, 0.5945751, 0.7218385, 0.8423202, 226.12, 717.38, 453.0), + doubleArrayOf(0.082676105, 0.77907515, 0.8244223, 0.9177246, 0.92081827, 223.85, 1179.0, 499.13), + doubleArrayOf(0.2799132, 0.44869733, 0.71312374, 0.803495, 0.8236593, 188.53, 654.0, 395.63), + doubleArrayOf(0.22300959, 0.35341972, 0.50126994, 0.5216613, 0.80473685, 393.8, 1073.75, 381.63), + doubleArrayOf(0.16872369, 0.44432908, 0.4945342, 0.5139766, 0.8993664, 171.06, 594.25, 454.63), + doubleArrayOf(0.17708255, 0.4663419, 0.53943986, 0.64106923, 0.76046133, 162.98, 541.25, 446.5), + doubleArrayOf(0.20545381, 0.45018867, 0.60665923, 0.6167865, 0.9388506, 149.72, 238.75, 427.25), + doubleArrayOf(0.080217935, 0.50220335, 0.5176477, 0.58036906, 0.6536133, 240.76, 365.25, 452.0), + doubleArrayOf(0.12742372, 0.5992795, 0.79773456, 0.82226735, 0.942405, 151.57, 314.38, 450.75), + doubleArrayOf(0.20293406, 0.6276678, 0.7239713, 0.7324315, 0.9404851, 260.05, 733.38, 502.25), + doubleArrayOf(0.40032506, 0.47528812, 0.4949649, 0.63654554, 0.6890982, 381.29, 103.25, 510.63), + doubleArrayOf(0.0613762, 0.48887974, 0.50347656, 0.57836145, 0.61202574, 301.53, 1182.38, 553.25), + doubleArrayOf(0.10952552, 0.18405987, 0.20926912, 0.8231408, 0.86796653, 160.37, 627.0, 659.88), + doubleArrayOf(0.082676105, 0.6585399, 0.67820233, 0.9177246, 0.96905917, 223.85, 1131.0, 557.25), + doubleArrayOf(0.3743402, 0.5289205, 0.6279637, 0.64201605, 0.65396124, 288.58, 176.0, 594.88), + doubleArrayOf(0.44896066, 0.63435477, 0.70992845, 0.7451902, 0.7699947, 240.62, 261.38, 577.25), + doubleArrayOf(0.21088836, 0.65227014, 0.7611402, 0.8042312, 0.9132892, 250.24, 726.38, 497.63), + doubleArrayOf(0.086017, 0.43057266, 0.8917693, 0.935772, 0.9746273, 204.19, 613.38, 559.88), + doubleArrayOf(0.27380574, 0.62004495, 0.7361518, 0.76662827, 0.82295257, 246.17, 164.0, 545.88), + doubleArrayOf(0.23991768, 0.64504075, 0.8638924, 0.8762333, 0.9352145, 280.94, 118.25, 462.25), + doubleArrayOf(0.294163, 0.609248, 0.63931024, 0.6733474, 0.75656414, 298.88, 657.63, 530.88), + doubleArrayOf(0.43917423, 0.55030274, 0.64823836, 0.6504235, 0.7368766, 316.9, 1142.0, 424.0), + ) - origQuads.forEachIndexed { i, q -> - with(quads[i]) { - ratios[0] shouldBe (q[0] plusOrMinus 1e-7) - ratios[1] shouldBe (q[1] plusOrMinus 1e-7) - ratios[2] shouldBe (q[2] plusOrMinus 1e-7) - ratios[3] shouldBe (q[3] plusOrMinus 1e-7) - ratios[4] shouldBe (q[4] plusOrMinus 1e-7) - largestDistance shouldBe (q[5] plusOrMinus 1e-2) - midPointX shouldBe (q[6] plusOrMinus 1e-1) - midPointY shouldBe (q[7] plusOrMinus 1e-1) + origQuads.forEachIndexed { i, q -> + with(quads[i]) { + ratios[0] shouldBe (q[0] plusOrMinus 1e-7) + ratios[1] shouldBe (q[1] plusOrMinus 1e-7) + ratios[2] shouldBe (q[2] plusOrMinus 1e-7) + ratios[3] shouldBe (q[3] plusOrMinus 1e-7) + ratios[4] shouldBe (q[4] plusOrMinus 1e-7) + largestDistance shouldBe (q[5] plusOrMinus 1e-2) + midPointX shouldBe (q[6] plusOrMinus 1e-1) + midPointY shouldBe (q[7] plusOrMinus 1e-1) - stars[0].x shouldBe (origQuadStars[i][0] plusOrMinus 1e-1) - stars[0].y shouldBe (origQuadStars[i][1] plusOrMinus 1e-1) - stars[1].x shouldBe (origQuadStars[i][2] plusOrMinus 1e-1) - stars[1].y shouldBe (origQuadStars[i][3] plusOrMinus 1e-1) - stars[2].x shouldBe (origQuadStars[i][4] plusOrMinus 1e-1) - stars[2].y shouldBe (origQuadStars[i][5] plusOrMinus 1e-1) - stars[3].x shouldBe (origQuadStars[i][6] plusOrMinus 1e-1) - stars[3].y shouldBe (origQuadStars[i][7] plusOrMinus 1e-1) - } + stars[0].x shouldBe (origQuadStars[i][0] plusOrMinus 1e-1) + stars[0].y shouldBe (origQuadStars[i][1] plusOrMinus 1e-1) + stars[1].x shouldBe (origQuadStars[i][2] plusOrMinus 1e-1) + stars[1].y shouldBe (origQuadStars[i][3] plusOrMinus 1e-1) + stars[2].x shouldBe (origQuadStars[i][4] plusOrMinus 1e-1) + stars[2].y shouldBe (origQuadStars[i][5] plusOrMinus 1e-1) + stars[3].x shouldBe (origQuadStars[i][6] plusOrMinus 1e-1) + stars[3].y shouldBe (origQuadStars[i][7] plusOrMinus 1e-1) } } } companion object { + @JvmStatic private val QUAD_DIR = homeDirectory.concat("Downloads", "watneyqdb") + @JvmStatic private val QUAD_DATABASE = CompactQuadDatabase(QUAD_DIR) + @JvmStatic private val SOLVER = WatneyPlateSolver(QUAD_DATABASE) + @JvmStatic private val M31_STARS = listOf( Star(358.5, 0.5), Star(530.0, 3.0), Star(957.0, 3.5), Star(971.5, 6.5), Star(399.5, 58.5), Star(487.5, 108.0), diff --git a/nebulosa-watney/src/test/kotlin/WatneyStarDetectorTest.kt b/nebulosa-watney/src/test/kotlin/WatneyStarDetectorTest.kt index 1bc8dee5a..b6e99dd38 100644 --- a/nebulosa-watney/src/test/kotlin/WatneyStarDetectorTest.kt +++ b/nebulosa-watney/src/test/kotlin/WatneyStarDetectorTest.kt @@ -2,33 +2,34 @@ import io.kotest.matchers.collections.shouldHaveSize import io.kotest.matchers.shouldBe import nebulosa.fits.fits import nebulosa.image.Image +import nebulosa.image.Image.Companion.asImage import nebulosa.image.algorithms.transformation.Draw import nebulosa.image.algorithms.transformation.convolution.Mean import nebulosa.stardetector.StarPoint -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.M6707HH +import nebulosa.test.NGC3344_COLOR_32_FITS +import nebulosa.test.save import nebulosa.watney.stardetector.WatneyStarDetector +import org.junit.jupiter.api.Test import java.awt.Color import java.awt.Graphics2D import kotlin.math.roundToInt -class WatneyStarDetectorTest : AbstractFitsAndXisfTest() { +class WatneyStarDetectorTest { - init { - val detector = WatneyStarDetector(computeHFD = true) + @Test + fun detectStars() { + var image = NGC3344_COLOR_32_FITS.fits().asImage() + var stars = DETECTOR.detect(image.transform(Mean)) + stars shouldHaveSize 1 + image.transform(ImageStarsDraw(stars)).save("color-detected-stars-1") + .second shouldBe "bb237ce03f7cc9e44e69a5354b7a6fd1" - "detect stars" { - var image = Image.open(NGC3344_COLOR_32_FITS.fits()) - var stars = detector.detect(image.transform(Mean)) - stars shouldHaveSize 1 - image.transform(ImageStarsDraw(stars)).save("color-detected-stars-1") - .second shouldBe "bb237ce03f7cc9e44e69a5354b7a6fd1" - - image = Image.open(M6707HH.fits()) - stars = detector.detect(image.transform(Mean)) - stars shouldHaveSize 870 - image.transform(ImageStarsDraw(stars)).save("color-detected-stars-870") - .second shouldBe "004e4d2b4d9725c5367f6865986f6756" - } + image = M6707HH.fits().asImage() + stars = DETECTOR.detect(image.transform(Mean)) + stars shouldHaveSize 870 + image.transform(ImageStarsDraw(stars)).save("color-detected-stars-870") + .second shouldBe "004e4d2b4d9725c5367f6865986f6756" } private data class ImageStarsDraw(private val stars: List) : Draw() { @@ -41,4 +42,9 @@ class WatneyStarDetectorTest : AbstractFitsAndXisfTest() { } } } + + companion object { + + @JvmStatic private val DETECTOR = WatneyStarDetector(computeHFD = true) + } } diff --git a/nebulosa-wcs/src/test/kotlin/LibWCSTest.kt b/nebulosa-wcs/src/test/kotlin/LibWCSTest.kt index 18e21d869..315101eca 100644 --- a/nebulosa-wcs/src/test/kotlin/LibWCSTest.kt +++ b/nebulosa-wcs/src/test/kotlin/LibWCSTest.kt @@ -1,70 +1,168 @@ -import io.kotest.core.annotation.EnabledIf -import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.doubles.plusOrMinus import io.kotest.matchers.shouldBe import nebulosa.fits.fits import nebulosa.image.format.ReadableHeader import nebulosa.math.formatHMS import nebulosa.math.formatSignedDMS -import nebulosa.test.NonGitHubOnlyCondition +import nebulosa.test.NonGitHubOnly import nebulosa.wcs.WCS +import org.junit.jupiter.api.Test import java.nio.file.Path import kotlin.random.Random // https://www.atnf.csiro.au/people/mcalabre/WCS/example_data.html -@EnabledIf(NonGitHubOnlyCondition::class) -class LibWCSTest : StringSpec() { +@NonGitHubOnly +class LibWCSTest { - init { - for (projection in PROJECTIONS) { - projection { - pixToSky(projection, 192, 192) - } - } + @Test + fun air() { + pixToSky("AIR", 192, 192) + } - "TAN-SIP" { - pixToSky("TAN-SIP", 1280, 720) - } + @Test + fun ait() { + pixToSky("AIT", 192, 192) } - private fun pixToSky(projectionName: String, width: Int, height: Int) { - val data = Array(2048) { intArrayOf(Random.nextInt(width), Random.nextInt(height)) } + @Test + fun arc() { + pixToSky("ARC", 192, 192) + } - WCS(readHeaderFromFits(projectionName)).use { - val topLeft = it.pixToSky(0.0, 0.0) - val topRight = it.pixToSky(width.toDouble(), 0.0) - val bottomLeft = it.pixToSky(0.0, height.toDouble()) - val bottomRight = it.pixToSky(width.toDouble(), height.toDouble()) - val center = it.pixToSky(width / 2.0, height / 2.0) + @Test + fun azp() { + pixToSky("AZP", 192, 192) + } - println("top left: ${topLeft.rightAscension.formatHMS()} ${topLeft.declination.formatSignedDMS()}") - println("top right: ${topRight.rightAscension.formatHMS()} ${topRight.declination.formatSignedDMS()}") - println("bottom left: ${bottomLeft.rightAscension.formatHMS()} ${bottomLeft.declination.formatSignedDMS()}") - println("bottom right: ${bottomRight.rightAscension.formatHMS()} ${bottomRight.declination.formatSignedDMS()}") - println("center: ${center.rightAscension.formatHMS()} ${center.declination.formatSignedDMS()}") + @Test + fun car() { + pixToSky("CAR", 192, 192) + } - for ((x0, y0) in data) { - val (rightAscension, declination) = it.pixToSky(x0.toDouble(), y0.toDouble()) - val (x1, y1) = it.skyToPix(rightAscension, declination) - x1 shouldBe (x0.toDouble() plusOrMinus 1.0) - y1 shouldBe (y0.toDouble() plusOrMinus 1.0) - } - } + @Test + fun cea() { + pixToSky("CEA", 192, 192) + } + + @Test + fun csc() { + pixToSky("CSC", 192, 192) + } + + @Test + fun cyp() { + pixToSky("CYP", 192, 192) + } + + @Test + fun hpx() { + pixToSky("HPX", 192, 192) + } + + @Test + fun mer() { + pixToSky("MER", 192, 192) + } + + @Test + fun mol() { + pixToSky("MOL", 192, 192) + } + + @Test + fun ncp() { + pixToSky("NCP", 192, 192) + } + + @Test + fun par() { + pixToSky("PAR", 192, 192) + } + + @Test + fun pco() { + pixToSky("PCO", 192, 192) } - private fun readHeaderFromFits(name: String): ReadableHeader { - return Path.of("src/test/resources/$name.fits").fits().use { it.first!!.header } + @Test + fun qsc() { + pixToSky("QSC", 192, 192) } + @Test + fun sfl() { + pixToSky("SFL", 192, 192) + } + + @Test + fun sin() { + pixToSky("SIN", 192, 192) + } + + @Test + fun stg() { + pixToSky("STG", 192, 192) + } + + @Test + fun szp() { + pixToSky("SZP", 192, 192) + } + + @Test + fun tan() { + pixToSky("TAN", 192, 192) + } + + @Test + fun tsc() { + pixToSky("TSC", 192, 192) + } + + @Test + fun zea() { + pixToSky("ZEA", 192, 192) + } + + @Test + fun tanSip() { + pixToSky("TAN-SIP", 1280, 720) + } + + // "BON", "COD", "COE", "COO", "COP", "ZPN" // FAILED + companion object { - @JvmStatic private val PROJECTIONS = arrayOf( - "AIR", "AIT", "ARC", "AZP", "CAR", "CEA", - "CSC", "CYP", "HPX", "MER", "MOL", - "NCP", "PAR", "PCO", "QSC", "SFL", "SIN", "STG", "SZP", - "TAN", "TSC", "ZEA", - "BON", "COD", "COE", "COO", "COP", "ZPN" // FAILED - ) + @JvmStatic + private fun pixToSky(projectionName: String, width: Int, height: Int) { + val data = Array(2048) { intArrayOf(Random.nextInt(width), Random.nextInt(height)) } + + WCS(readHeaderFromFits(projectionName)).use { + val topLeft = it.pixToSky(0.0, 0.0) + val topRight = it.pixToSky(width.toDouble(), 0.0) + val bottomLeft = it.pixToSky(0.0, height.toDouble()) + val bottomRight = it.pixToSky(width.toDouble(), height.toDouble()) + val center = it.pixToSky(width / 2.0, height / 2.0) + + println("top left: ${topLeft.rightAscension.formatHMS()} ${topLeft.declination.formatSignedDMS()}") + println("top right: ${topRight.rightAscension.formatHMS()} ${topRight.declination.formatSignedDMS()}") + println("bottom left: ${bottomLeft.rightAscension.formatHMS()} ${bottomLeft.declination.formatSignedDMS()}") + println("bottom right: ${bottomRight.rightAscension.formatHMS()} ${bottomRight.declination.formatSignedDMS()}") + println("center: ${center.rightAscension.formatHMS()} ${center.declination.formatSignedDMS()}") + + for ((x0, y0) in data) { + val (rightAscension, declination) = it.pixToSky(x0.toDouble(), y0.toDouble()) + val (x1, y1) = it.skyToPix(rightAscension, declination) + x1 shouldBe (x0.toDouble() plusOrMinus 1.0) + y1 shouldBe (y0.toDouble() plusOrMinus 1.0) + } + } + } + + @JvmStatic + private fun readHeaderFromFits(name: String): ReadableHeader { + return Path.of("src/test/resources/$name.fits").fits().use { it.first!!.header } + } } } diff --git a/nebulosa-xisf/src/test/kotlin/CompressionByteShufflerTest.kt b/nebulosa-xisf/src/test/kotlin/CompressionByteShufflerTest.kt index 66556a1ad..560b087de 100644 --- a/nebulosa-xisf/src/test/kotlin/CompressionByteShufflerTest.kt +++ b/nebulosa-xisf/src/test/kotlin/CompressionByteShufflerTest.kt @@ -1,30 +1,31 @@ -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.ints.shouldBeExactly +import io.kotest.matchers.shouldBe import nebulosa.xisf.CompressionByteShuffler +import org.junit.jupiter.api.Test import kotlin.random.Random -class CompressionByteShufflerTest : StringSpec() { +class CompressionByteShufflerTest { - init { - "shuffle & unshuffle" { - val original = ByteArray(256) { (it / 32).toByte() } - val buffer = ByteArray(256) - val unshuffled = ByteArray(256) + @Test + fun shuffleAndUnshuffle() { + val original = ByteArray(256) { (it / 32).toByte() } + val buffer = ByteArray(256) + val unshuffled = ByteArray(256) - CompressionByteShuffler.shuffle(original, buffer, 8) - CompressionByteShuffler.unshuffle(buffer, unshuffled, 8) + CompressionByteShuffler.shuffle(original, buffer, 8) + CompressionByteShuffler.unshuffle(buffer, unshuffled, 8) - repeat(original.size) { unshuffled[it].toInt() shouldBeExactly original[it].toInt() } - } - "random shuffle & unshuffle" { - val original = Random.nextBytes(256) - val buffer = ByteArray(256) - val unshuffled = ByteArray(256) + unshuffled shouldBe original + } + + @Test + fun randomShuffleAndUnshuffle() { + val original = Random.nextBytes(256) + val buffer = ByteArray(256) + val unshuffled = ByteArray(256) - CompressionByteShuffler.shuffle(original, buffer, 8) - CompressionByteShuffler.unshuffle(buffer, unshuffled, 8) + CompressionByteShuffler.shuffle(original, buffer, 8) + CompressionByteShuffler.unshuffle(buffer, unshuffled, 8) - repeat(original.size) { unshuffled[it].toInt() shouldBeExactly original[it].toInt() } - } + unshuffled shouldBe original } } diff --git a/nebulosa-xisf/src/test/kotlin/XisfFormatTest.kt b/nebulosa-xisf/src/test/kotlin/XisfFormatTest.kt index 79ac306e4..4eaa339d2 100644 --- a/nebulosa-xisf/src/test/kotlin/XisfFormatTest.kt +++ b/nebulosa-xisf/src/test/kotlin/XisfFormatTest.kt @@ -1,8 +1,6 @@ -import io.kotest.engine.spec.tempfile import io.kotest.matchers.booleans.shouldBeFalse import io.kotest.matchers.booleans.shouldBeTrue import io.kotest.matchers.collections.shouldHaveSize -import io.kotest.matchers.floats.shouldBeExactly import io.kotest.matchers.ints.shouldBeExactly import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeSameInstanceAs @@ -10,403 +8,438 @@ import io.kotest.matchers.types.shouldNotBeSameInstanceAs import nebulosa.fits.FitsFormat import nebulosa.fits.bitpix import nebulosa.image.format.ImageHdu +import nebulosa.image.format.ImageHdu.Companion.makeImage import nebulosa.io.seekableSink import nebulosa.io.seekableSource -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.* import nebulosa.xisf.XisfFormat import nebulosa.xisf.isXisf +import org.junit.jupiter.api.Test -class XisfFormatTest : AbstractFitsAndXisfTest() { +class XisfFormatTest : AbstractTest() { - init { - "should be xisf format" { - NGC3344_COLOR_8_FITS.isXisf().shouldBeFalse() - M82_COLOR_16_XISF.isXisf().shouldBeTrue() - } - "mono:planar:8" { - val source = closeAfterEach(M82_MONO_8_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - header shouldHaveSize 23 - data.red.size shouldBeExactly 512 * 512 - data.green shouldBeSameInstanceAs data.red - data.blue shouldBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-mono-planar-8").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } + @Test + fun shouldBeXisfFormat() { + NGC3344_COLOR_8_FITS.isXisf().shouldBeFalse() + M82_COLOR_16_XISF.isXisf().shouldBeTrue() + } + + @Test + fun monoPlanar8Bit() { + val source = M82_MONO_8_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + header shouldHaveSize 23 + data.red.size shouldBeExactly 512 * 512 + data.green shouldBeSameInstanceAs data.red + data.blue shouldBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-mono-planar-8").second shouldBe "0dca7efedef5b3525f8037f401518b0b" } - "mono:planar:16" { - val source = closeAfterEach(M82_MONO_16_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - header shouldHaveSize 23 - data.red.size shouldBeExactly 512 * 512 - data.green shouldBeSameInstanceAs data.red - data.blue shouldBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-mono-planar-16").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } + } + + @Test + fun monoPlanar16Bit() { + val source = M82_MONO_16_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + header shouldHaveSize 23 + data.red.size shouldBeExactly 512 * 512 + data.green shouldBeSameInstanceAs data.red + data.blue shouldBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-mono-planar-16").second shouldBe "0dca7efedef5b3525f8037f401518b0b" } - "mono:planar:32" { - val source = closeAfterEach(M82_MONO_32_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - header shouldHaveSize 23 - data.red.size shouldBeExactly 512 * 512 - data.green shouldBeSameInstanceAs data.red - data.blue shouldBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-mono-planar-32").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } + } + + @Test + fun monoPlanar32Bit() { + val source = M82_MONO_32_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + header shouldHaveSize 23 + data.red.size shouldBeExactly 512 * 512 + data.green shouldBeSameInstanceAs data.red + data.blue shouldBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-mono-planar-32").second shouldBe "0dca7efedef5b3525f8037f401518b0b" } - "mono:planar:F32" { - val source = closeAfterEach(M82_MONO_F32_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - header shouldHaveSize 23 - data.red.size shouldBeExactly 512 * 512 - data.green shouldBeSameInstanceAs data.red - data.blue shouldBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-mono-planar-F32").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } + } + + @Test + fun monoPlanarFloat32Bit() { + val source = M82_MONO_F32_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + header shouldHaveSize 23 + data.red.size shouldBeExactly 512 * 512 + data.green shouldBeSameInstanceAs data.red + data.blue shouldBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-mono-planar-F32").second shouldBe "0dca7efedef5b3525f8037f401518b0b" } - "mono:planar:F64" { - val source = closeAfterEach(M82_MONO_F64_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - header shouldHaveSize 23 - data.red.size shouldBeExactly 512 * 512 - data.green shouldBeSameInstanceAs data.red - data.blue shouldBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-mono-planar-F64").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } + } + + @Test + fun monoPlanarFloat64() { + val source = M82_MONO_F64_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + header shouldHaveSize 23 + data.red.size shouldBeExactly 512 * 512 + data.green shouldBeSameInstanceAs data.red + data.blue shouldBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-mono-planar-F64").second shouldBe "0dca7efedef5b3525f8037f401518b0b" } - "color:planar:8" { - val source = closeAfterEach(M82_COLOR_8_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - header shouldHaveSize 24 - data.red.size shouldBeExactly 512 * 512 - data.green shouldNotBeSameInstanceAs data.red - data.blue shouldNotBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-color-planar-8").second shouldBe "764e326cc5260d81f3761112ad6a1969" - } + } + + @Test + fun colorPlanar8Bit() { + val source = M82_COLOR_8_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + header shouldHaveSize 24 + data.red.size shouldBeExactly 512 * 512 + data.green shouldNotBeSameInstanceAs data.red + data.blue shouldNotBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-color-planar-8").second shouldBe "764e326cc5260d81f3761112ad6a1969" } - "color:planar:16" { - val source = closeAfterEach(M82_COLOR_16_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - header shouldHaveSize 24 - data.red.size shouldBeExactly 512 * 512 - data.green shouldNotBeSameInstanceAs data.red - data.blue shouldNotBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-color-planar-16").second shouldBe "764e326cc5260d81f3761112ad6a1969" - } + } + + @Test + fun colorPlanar16Bit() { + val source = M82_COLOR_16_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + header shouldHaveSize 24 + data.red.size shouldBeExactly 512 * 512 + data.green shouldNotBeSameInstanceAs data.red + data.blue shouldNotBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-color-planar-16").second shouldBe "764e326cc5260d81f3761112ad6a1969" } - "color:planar:32" { - val source = closeAfterEach(M82_COLOR_32_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - header shouldHaveSize 24 - data.red.size shouldBeExactly 512 * 512 - data.green shouldNotBeSameInstanceAs data.red - data.blue shouldNotBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-color-planar-32").second shouldBe "764e326cc5260d81f3761112ad6a1969" - } + } + + @Test + fun colorPlanar32Bit() { + val source = M82_COLOR_32_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + header shouldHaveSize 24 + data.red.size shouldBeExactly 512 * 512 + data.green shouldNotBeSameInstanceAs data.red + data.blue shouldNotBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-color-planar-32").second shouldBe "764e326cc5260d81f3761112ad6a1969" } - "color:planar:F32" { - val source = closeAfterEach(M82_COLOR_F32_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - header shouldHaveSize 24 - data.red.size shouldBeExactly 512 * 512 - data.green shouldNotBeSameInstanceAs data.red - data.blue shouldNotBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-color-planar-F32").second shouldBe "764e326cc5260d81f3761112ad6a1969" - } + } + + @Test + fun colorPlanarFloat32Bit() { + val source = M82_COLOR_F32_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + header shouldHaveSize 24 + data.red.size shouldBeExactly 512 * 512 + data.green shouldNotBeSameInstanceAs data.red + data.blue shouldNotBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-color-planar-F32").second shouldBe "764e326cc5260d81f3761112ad6a1969" } - "color:planar:F64" { - val source = closeAfterEach(M82_COLOR_F64_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - header shouldHaveSize 24 - data.red.size shouldBeExactly 512 * 512 - data.green shouldNotBeSameInstanceAs data.red - data.blue shouldNotBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-color-planar-F64").second shouldBe "764e326cc5260d81f3761112ad6a1969" - } + } + + @Test + fun colorPlanarFloat64() { + val source = M82_COLOR_F64_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + header shouldHaveSize 24 + data.red.size shouldBeExactly 512 * 512 + data.green shouldNotBeSameInstanceAs data.red + data.blue shouldNotBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-color-planar-F64").second shouldBe "764e326cc5260d81f3761112ad6a1969" } - "mono:planar:8:zlib" { - val source = closeAfterEach(M82_MONO_8_ZLIB_XISF.seekableSource()) - val hdus = XisfFormat.read(source) - - hdus shouldHaveSize 1 - - with(hdus[0]) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - header shouldHaveSize 23 - data.red.size shouldBeExactly 512 * 512 - data.green shouldBeSameInstanceAs data.red - data.blue shouldBeSameInstanceAs data.green - - val image = makeImage() - image.save("xisf-mono-planar-8-zlib").second shouldBe "0dca7efedef5b3525f8037f401518b0b" - } + } + + @Test + fun monoPlanar8BitZLib() { + val source = M82_MONO_8_ZLIB_XISF.seekableSource().autoClose() + val hdus = XisfFormat.read(source) + + hdus shouldHaveSize 1 + + with(hdus[0]) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + header shouldHaveSize 23 + data.red.size shouldBeExactly 512 * 512 + data.green shouldBeSameInstanceAs data.red + data.blue shouldBeSameInstanceAs data.green + + val image = makeImage() + image.save("xisf-mono-planar-8-zlib").second shouldBe "0dca7efedef5b3525f8037f401518b0b" } - "mono:write" { - val formats = arrayOf(PALETTE_MONO_8_XISF, PALETTE_MONO_16_XISF, PALETTE_MONO_32_XISF, PALETTE_MONO_F32_XISF, PALETTE_MONO_F64_XISF) - - for (format in formats) { - val source0 = closeAfterEach(format.seekableSource()) - val hdus0 = XisfFormat.read(source0) - - val outputPath = tempfile() - val sink = closeAfterEach(outputPath.seekableSink()) - XisfFormat.write(sink, hdus0) - - val source1 = closeAfterEach(outputPath.seekableSource()) - val hdus1 = XisfFormat.read(source1) - - hdus1 shouldHaveSize 1 - hdus1[0].data.numberOfChannels shouldBeExactly 1 - hdus1[0].header.size shouldBeExactly hdus0[0].header.size - hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size - hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red - hdus1[0].data.red.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.red[i] } - - val bitpix = hdus1[0].header.bitpix - val image = hdus1[0].makeImage() - image.save("xisf-mono-write-$bitpix").second shouldBe "07762064ff54ccc7771ba5b34fca86cf" - } + } + + @Test + fun monoWrite() { + val formats = arrayOf(PALETTE_MONO_8_XISF, PALETTE_MONO_16_XISF, PALETTE_MONO_32_XISF, PALETTE_MONO_F32_XISF, PALETTE_MONO_F64_XISF) + + for (format in formats) { + val source0 = format.seekableSource().autoClose() + val hdus0 = XisfFormat.read(source0) + + val outputPath = tempPath("mono-", ".xisf") + val sink = outputPath.seekableSink().autoClose() + XisfFormat.write(sink, hdus0) + + val source1 = outputPath.seekableSource().autoClose() + val hdus1 = XisfFormat.read(source1) + + hdus1 shouldHaveSize 1 + hdus1[0].data.numberOfChannels shouldBeExactly 1 + hdus1[0].header.size shouldBeExactly hdus0[0].header.size + hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size + hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red + hdus1[0].data.red shouldBe hdus0[0].data.red + + val bitpix = hdus1[0].header.bitpix + val image = hdus1[0].makeImage() + image.save("xisf-mono-write-$bitpix").second shouldBe "07762064ff54ccc7771ba5b34fca86cf" } - "color:write" { - val formats = arrayOf(PALETTE_COLOR_8_XISF, PALETTE_COLOR_16_XISF, PALETTE_COLOR_32_XISF, PALETTE_COLOR_F32_XISF, PALETTE_COLOR_F64_XISF) - - for (format in formats) { - val source0 = closeAfterEach(format.seekableSource()) - val hdus0 = XisfFormat.read(source0) - - val outputPath = tempfile() - val sink = closeAfterEach(outputPath.seekableSink()) - XisfFormat.write(sink, hdus0) - - val source1 = closeAfterEach(outputPath.seekableSource()) - val hdus1 = XisfFormat.read(source1) - - hdus1 shouldHaveSize 1 - hdus1[0].data.numberOfChannels shouldBeExactly 3 - hdus1[0].header.size shouldBeExactly hdus0[0].header.size - hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size - hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red - hdus1[0].data.red.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.red[i] } - hdus1[0].data.green shouldNotBeSameInstanceAs hdus0[0].data.green - hdus1[0].data.green.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.green[i] } - hdus1[0].data.blue shouldNotBeSameInstanceAs hdus0[0].data.blue - hdus1[0].data.blue.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.blue[i] } - - val bitpix = hdus1[0].header.bitpix - val image = hdus1[0].makeImage() - image.save("xisf-color-write-$bitpix").second shouldBe "7233886f62065800b43419f3b1b6c833" - } + } + + @Test + fun colorWrite() { + val formats = arrayOf(PALETTE_COLOR_8_XISF, PALETTE_COLOR_16_XISF, PALETTE_COLOR_32_XISF, PALETTE_COLOR_F32_XISF, PALETTE_COLOR_F64_XISF) + + for (format in formats) { + val source0 = format.seekableSource().autoClose() + val hdus0 = XisfFormat.read(source0) + + val outputPath = tempPath("color-", ".xisf") + val sink = outputPath.seekableSink().autoClose() + XisfFormat.write(sink, hdus0) + + val source1 = outputPath.seekableSource().autoClose() + val hdus1 = XisfFormat.read(source1) + + hdus1 shouldHaveSize 1 + hdus1[0].data.numberOfChannels shouldBeExactly 3 + hdus1[0].header.size shouldBeExactly hdus0[0].header.size + hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size + hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red + hdus1[0].data.red shouldBe hdus0[0].data.red + hdus1[0].data.green shouldNotBeSameInstanceAs hdus0[0].data.green + hdus1[0].data.green shouldBe hdus0[0].data.green + hdus1[0].data.blue shouldNotBeSameInstanceAs hdus0[0].data.blue + hdus1[0].data.blue shouldBe hdus0[0].data.blue + + val bitpix = hdus1[0].header.bitpix + val image = hdus1[0].makeImage() + image.save("xisf-color-write-$bitpix").second shouldBe "7233886f62065800b43419f3b1b6c833" } - "fits-to-xisf:mono" { - val formats = arrayOf(PALETTE_MONO_8_FITS, PALETTE_MONO_16_FITS, PALETTE_MONO_32_FITS, PALETTE_MONO_F32_FITS, PALETTE_MONO_F64_FITS) + } + + @Test + fun fitsToXisfMono() { + val formats = arrayOf(PALETTE_MONO_8_FITS, PALETTE_MONO_16_FITS, PALETTE_MONO_32_FITS, PALETTE_MONO_F32_FITS, PALETTE_MONO_F64_FITS) - for (format in formats) { - val source0 = closeAfterEach(format.seekableSource()) - val hdus0 = FitsFormat.read(source0).filterIsInstance() + for (format in formats) { + val source0 = format.seekableSource().autoClose() + val hdus0 = FitsFormat.read(source0).filterIsInstance() - hdus0 shouldHaveSize 1 + hdus0 shouldHaveSize 1 - val outputPath = tempfile() - val sink = closeAfterEach(outputPath.seekableSink()) - XisfFormat.write(sink, hdus0) + val outputPath = tempPath("fits-xisf-mono-", ".xisf") + val sink = outputPath.seekableSink().autoClose() + XisfFormat.write(sink, hdus0) - val source1 = closeAfterEach(outputPath.seekableSource()) - val hdus1 = XisfFormat.read(source1) + val source1 = outputPath.seekableSource().autoClose() + val hdus1 = XisfFormat.read(source1) - hdus1 shouldHaveSize 1 - hdus1[0].data.numberOfChannels shouldBeExactly 1 - // hdus1[0].header.size shouldBeExactly hdus0[0].header.size - hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size - hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red - hdus1[0].data.red.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.red[i] } + hdus1 shouldHaveSize 1 + hdus1[0].data.numberOfChannels shouldBeExactly 1 + // hdus1[0].header.size shouldBeExactly hdus0[0].header.size + hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size + hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red + hdus1[0].data.red shouldBe hdus0[0].data.red - val bitpix = hdus1[0].header.bitpix - val image = hdus1[0].makeImage() - image.save("fits-to-xisf-mono-$bitpix").second shouldBe "07762064ff54ccc7771ba5b34fca86cf" - } + val bitpix = hdus1[0].header.bitpix + val image = hdus1[0].makeImage() + image.save("fits-to-xisf-mono-$bitpix").second shouldBe "07762064ff54ccc7771ba5b34fca86cf" } - "fits-to-xisf:color" { - val formats = arrayOf(PALETTE_COLOR_8_FITS, PALETTE_COLOR_16_FITS, PALETTE_COLOR_32_FITS, PALETTE_COLOR_F32_FITS, PALETTE_COLOR_F64_FITS) - - for (format in formats) { - val source0 = closeAfterEach(format.seekableSource()) - val hdus0 = FitsFormat.read(source0).filterIsInstance() - - hdus0 shouldHaveSize 1 - - val outputPath = tempfile() - val sink = closeAfterEach(outputPath.seekableSink()) - XisfFormat.write(sink, hdus0) - - val source1 = closeAfterEach(outputPath.seekableSource()) - val hdus1 = XisfFormat.read(source1) - - hdus1 shouldHaveSize 1 - hdus1[0].data.numberOfChannels shouldBeExactly 3 - // hdus1[0].header.size shouldBeExactly hdus0[0].header.size - hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size - hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red - hdus1[0].data.red.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.red[i] } - hdus1[0].data.green shouldNotBeSameInstanceAs hdus0[0].data.green - hdus1[0].data.green.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.green[i] } - hdus1[0].data.blue shouldNotBeSameInstanceAs hdus0[0].data.blue - hdus1[0].data.blue.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.blue[i] } - - val bitpix = hdus1[0].header.bitpix - val image = hdus1[0].makeImage() - image.save("fits-to-xisf-color-$bitpix").second shouldBe "7233886f62065800b43419f3b1b6c833" - } + } + + @Test + fun fitsToXisfColor() { + val formats = arrayOf(PALETTE_COLOR_8_FITS, PALETTE_COLOR_16_FITS, PALETTE_COLOR_32_FITS, PALETTE_COLOR_F32_FITS, PALETTE_COLOR_F64_FITS) + + for (format in formats) { + val source0 = format.seekableSource().autoClose() + val hdus0 = FitsFormat.read(source0).filterIsInstance() + + hdus0 shouldHaveSize 1 + + val outputPath = tempPath("fits-xisf-color", ".xisf") + val sink = outputPath.seekableSink().autoClose() + XisfFormat.write(sink, hdus0) + + val source1 = outputPath.seekableSource().autoClose() + val hdus1 = XisfFormat.read(source1) + + hdus1 shouldHaveSize 1 + hdus1[0].data.numberOfChannels shouldBeExactly 3 + // hdus1[0].header.size shouldBeExactly hdus0[0].header.size + hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size + hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red + hdus1[0].data.red shouldBe hdus0[0].data.red + hdus1[0].data.green shouldNotBeSameInstanceAs hdus0[0].data.green + hdus1[0].data.green shouldBe hdus0[0].data.green + hdus1[0].data.blue shouldNotBeSameInstanceAs hdus0[0].data.blue + hdus1[0].data.blue shouldBe hdus0[0].data.blue + + val bitpix = hdus1[0].header.bitpix + val image = hdus1[0].makeImage() + image.save("fits-to-xisf-color-$bitpix").second shouldBe "7233886f62065800b43419f3b1b6c833" } - "xisf-to-fits:mono" { - val formats = arrayOf(PALETTE_MONO_8_XISF, PALETTE_MONO_16_XISF, PALETTE_MONO_32_XISF, PALETTE_MONO_F32_XISF, PALETTE_MONO_F64_XISF) + } + + @Test + fun xisfToFitsMono() { + val formats = arrayOf(PALETTE_MONO_8_XISF, PALETTE_MONO_16_XISF, PALETTE_MONO_32_XISF, PALETTE_MONO_F32_XISF, PALETTE_MONO_F64_XISF) - for (format in formats) { - val source0 = closeAfterEach(format.seekableSource()) - val hdus0 = XisfFormat.read(source0) + for (format in formats) { + val source0 = format.seekableSource().autoClose() + val hdus0 = XisfFormat.read(source0) - hdus0 shouldHaveSize 1 + hdus0 shouldHaveSize 1 - val outputPath = tempfile() - val sink = closeAfterEach(outputPath.seekableSink()) - FitsFormat.write(sink, hdus0) + val outputPath = tempPath("xisf-fits-mono", ".fits") + val sink = outputPath.seekableSink().autoClose() + FitsFormat.write(sink, hdus0) - val source1 = closeAfterEach(outputPath.seekableSource()) - val hdus1 = FitsFormat.read(source1).filterIsInstance() + val source1 = outputPath.seekableSource().autoClose() + val hdus1 = FitsFormat.read(source1).filterIsInstance() - hdus1 shouldHaveSize 1 - hdus1[0].data.numberOfChannels shouldBeExactly 1 - // hdus1[0].header.size shouldBeExactly hdus0[0].header.size - hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size - hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red - hdus1[0].data.red.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.red[i] } + hdus1 shouldHaveSize 1 + hdus1[0].data.numberOfChannels shouldBeExactly 1 + // hdus1[0].header.size shouldBeExactly hdus0[0].header.size + hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size + hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red + hdus1[0].data.red shouldBe hdus0[0].data.red - val bitpix = hdus1[0].header.bitpix - val image = hdus1[0].makeImage() - image.save("xisf-to-fits-mono-$bitpix").second shouldBe "07762064ff54ccc7771ba5b34fca86cf" - } + val bitpix = hdus1[0].header.bitpix + val image = hdus1[0].makeImage() + image.save("xisf-to-fits-mono-$bitpix").second shouldBe "07762064ff54ccc7771ba5b34fca86cf" } - "xisf-to-fits:color" { - val formats = arrayOf(PALETTE_COLOR_8_XISF, PALETTE_COLOR_16_XISF, PALETTE_COLOR_32_XISF, PALETTE_COLOR_F32_XISF, PALETTE_COLOR_F64_XISF) - - for (format in formats) { - val source0 = closeAfterEach(format.seekableSource()) - val hdus0 = XisfFormat.read(source0) - - hdus0 shouldHaveSize 1 - - val outputPath = tempfile() - val sink = closeAfterEach(outputPath.seekableSink()) - FitsFormat.write(sink, hdus0) - - val source1 = closeAfterEach(outputPath.seekableSource()) - val hdus1 = FitsFormat.read(source1).filterIsInstance() - - hdus1 shouldHaveSize 1 - hdus1[0].data.numberOfChannels shouldBeExactly 3 - // hdus1[0].header.size shouldBeExactly hdus0[0].header.size - hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size - hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red - hdus1[0].data.red.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.red[i] } - hdus1[0].data.green shouldNotBeSameInstanceAs hdus0[0].data.green - hdus1[0].data.green.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.green[i] } - hdus1[0].data.blue shouldNotBeSameInstanceAs hdus0[0].data.blue - hdus1[0].data.blue.forEachIndexed { i, value -> value shouldBeExactly hdus0[0].data.blue[i] } - - val bitpix = hdus1[0].header.bitpix - val image = hdus1[0].makeImage() - image.save("xisf-to-fits-color-$bitpix").second shouldBe "7233886f62065800b43419f3b1b6c833" - } + } + + @Test + fun xisfToFitsColor() { + val formats = arrayOf(PALETTE_COLOR_8_XISF, PALETTE_COLOR_16_XISF, PALETTE_COLOR_32_XISF, PALETTE_COLOR_F32_XISF, PALETTE_COLOR_F64_XISF) + + for (format in formats) { + val source0 = format.seekableSource().autoClose() + val hdus0 = XisfFormat.read(source0) + + hdus0 shouldHaveSize 1 + + val outputPath = tempPath("xisf-fits-color", ".fits") + val sink = outputPath.seekableSink().autoClose() + FitsFormat.write(sink, hdus0) + + val source1 = outputPath.seekableSource().autoClose() + val hdus1 = FitsFormat.read(source1).filterIsInstance() + + hdus1 shouldHaveSize 1 + hdus1[0].data.numberOfChannels shouldBeExactly 3 + // hdus1[0].header.size shouldBeExactly hdus0[0].header.size + hdus1[0].data.red.size shouldBeExactly hdus0[0].data.red.size + hdus1[0].data.red shouldNotBeSameInstanceAs hdus0[0].data.red + hdus1[0].data.red shouldBe hdus0[0].data.red + hdus1[0].data.green shouldNotBeSameInstanceAs hdus0[0].data.green + hdus1[0].data.green shouldBe hdus0[0].data.green + hdus1[0].data.blue shouldNotBeSameInstanceAs hdus0[0].data.blue + hdus1[0].data.blue shouldBe hdus0[0].data.blue + + val bitpix = hdus1[0].header.bitpix + val image = hdus1[0].makeImage() + image.save("xisf-to-fits-color-$bitpix").second shouldBe "7233886f62065800b43419f3b1b6c833" } } } diff --git a/nebulosa-xisf/src/test/kotlin/XisfHeaderInputStreamTest.kt b/nebulosa-xisf/src/test/kotlin/XisfHeaderInputStreamTest.kt index 457aa4c2f..025ef5b36 100644 --- a/nebulosa-xisf/src/test/kotlin/XisfHeaderInputStreamTest.kt +++ b/nebulosa-xisf/src/test/kotlin/XisfHeaderInputStreamTest.kt @@ -4,227 +4,253 @@ import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeInstanceOf import nebulosa.io.ByteOrder -import nebulosa.test.AbstractFitsAndXisfTest +import nebulosa.test.* import nebulosa.xisf.XisfHeaderInputStream import nebulosa.xisf.XisfMonolithicFileHeader +import org.junit.jupiter.api.Test import kotlin.io.path.inputStream -class XisfHeaderInputStreamTest : AbstractFitsAndXisfTest() { - - init { - "read:8:gray" { - val stream = closeAfterEach(M82_MONO_8_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 23 - } +class XisfHeaderInputStreamTest : AbstractTest() { + + @Test + fun read8BitGray() { + val stream = M82_MONO_8_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 23 } - "read:8:gray:zlib" { - val stream = closeAfterEach(M82_MONO_8_ZLIB_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.ZLIB - keywords shouldHaveSize 23 - } + } + + @Test + fun read8BitGrayZLib() { + val stream = M82_MONO_8_ZLIB_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.ZLIB + keywords shouldHaveSize 23 } - "read:8:gray:lz4" { - val stream = closeAfterEach(M82_MONO_8_LZ4_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.LZ4 - keywords shouldHaveSize 23 - } + } + + @Test + fun read8BitGrayLz4() { + val stream = M82_MONO_8_LZ4_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.LZ4 + keywords shouldHaveSize 23 } - "read:8:gray:lz4-hc" { - val stream = closeAfterEach(M82_MONO_8_LZ4_HC_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.LZ4_HC - keywords shouldHaveSize 23 - } + } + + @Test + fun read8BitGrayLz4HC() { + val stream = M82_MONO_8_LZ4_HC_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.LZ4_HC + keywords shouldHaveSize 23 } - "read:8:gray:zstd" { - val stream = closeAfterEach(M82_MONO_8_ZSTANDARD_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.ZSTD - keywords shouldHaveSize 23 - } + } + + @Test + fun read8BitGrayZStd() { + val stream = M82_MONO_8_ZSTANDARD_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + compressionFormat.shouldNotBeNull().type shouldBe XisfMonolithicFileHeader.CompressionType.ZSTD + keywords shouldHaveSize 23 } - "read:16:gray" { - val stream = closeAfterEach(M82_MONO_16_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT16 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 23 - } + } + + @Test + fun read16BitGray() { + val stream = M82_MONO_16_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT16 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 23 } - "read:32:gray" { - val stream = closeAfterEach(M82_MONO_32_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT32 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 23 - } + } + + @Test + fun read32BitGray() { + val stream = M82_MONO_32_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT32 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 23 } - "read:F32:gray" { - val stream = closeAfterEach(M82_MONO_F32_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT32 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 23 - } + } + + @Test + fun readFloat32Gray() { + val stream = M82_MONO_F32_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT32 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 23 } - "read:F64:gray" { - val stream = closeAfterEach(M82_MONO_F64_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 1 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT64 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 23 - } + } + + @Test + fun readFloat64Gray() { + val stream = M82_MONO_F64_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 1 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT64 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.GRAY + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 23 } - "read:8:rgb" { - val stream = closeAfterEach(M82_COLOR_8_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 24 - } + } + + @Test + fun read8BitRGB() { + val stream = M82_COLOR_8_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT8 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 24 } - "read:16:rgb" { - val stream = closeAfterEach(M82_COLOR_16_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT16 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 24 - } + } + + @Test + fun read16BitRGB() { + val stream = M82_COLOR_16_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT16 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 24 } - "read:32:rgb" { - val stream = closeAfterEach(M82_COLOR_32_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT32 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 24 - } + } + + @Test + fun read32BitRGB() { + val stream = M82_COLOR_32_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.UINT32 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 24 } - "read:F32:rgb" { - val stream = closeAfterEach(M82_COLOR_F32_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT32 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 24 - } + } + + @Test + fun readFloat32RGB() { + val stream = M82_COLOR_F32_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT32 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 24 } - "read:F64:rgb" { - val stream = closeAfterEach(M82_COLOR_F64_XISF.inputStream().also { it.skip(16) }) - val headerStream = XisfHeaderInputStream(stream) - val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() - - with(image) { - width shouldBeExactly 512 - height shouldBeExactly 512 - numberOfChannels shouldBeExactly 3 - sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT64 - colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB - byteOrder shouldBe ByteOrder.LITTLE - keywords shouldHaveSize 24 - } + } + + @Test + fun readFloat64RGB() { + val stream = M82_COLOR_F64_XISF.inputStream().also { it.skip(16) }.autoClose() + val headerStream = XisfHeaderInputStream(stream) + val image = headerStream.read().shouldNotBeNull().shouldBeInstanceOf() + + with(image) { + width shouldBeExactly 512 + height shouldBeExactly 512 + numberOfChannels shouldBeExactly 3 + sampleFormat shouldBe XisfMonolithicFileHeader.SampleFormat.FLOAT64 + colorSpace shouldBe XisfMonolithicFileHeader.ColorSpace.RGB + byteOrder shouldBe ByteOrder.LITTLE + keywords shouldHaveSize 24 } } } diff --git a/settings.gradle.kts b/settings.gradle.kts index 95058fe59..6ff001453 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -34,9 +34,9 @@ dependencyResolutionManagement { library("apache-numbers-complex", "org.apache.commons:commons-numbers-complex:1.1") library("oshi", "com.github.oshi:oshi-core:6.6.1") library("jna", "net.java.dev.jna:jna:5.14.0") - library("kotest-assertions-core", "io.kotest:kotest-assertions-core:5.9.1") - library("kotest-runner-junit5", "io.kotest:kotest-runner-junit5:5.9.1") - bundle("kotest", listOf("kotest-assertions-core", "kotest-runner-junit5")) + library("kotest", "io.kotest:kotest-assertions-core:5.9.1") + library("junit-api", "org.junit.jupiter:junit-jupiter-api:5.10.3") + library("junit-engine", "org.junit.jupiter:junit-jupiter-engine:5.10.3") bundle("netty", listOf("netty-transport", "netty-codec")) bundle("jackson", listOf("jackson-core", "jackson-jsr310", "jackson-kt")) }