feat: 引入可切换星历模型并扩展天象观测链路

- 新增半解析、旧模型、自定义与多源堆叠星历实例,提供 Context、Instance、SPK 读取及状态与视位置计算
- 统一太阳、月球和行星实例接口,扩展黄经穿越、留、相位、最近角距、偕日可见、任意高度升落、轨道节点及凌日等天象查询
- 完善日月食、月掩与凌日的月缘剖面、有限盘面接触、站心修正、局地可见性、全球路径和地图输出
- 新增 SVG、GeoJSON、KML 天象输出及凌日、月掩和月缘地形相关缓存与几何求解
- 新增恒星星表导入与查询,支持 CSV、Hipparcos、BSC5、Stellarium 等格式及名称、交叉标识、锥面检索和星等过滤
- 补充 IAU 2006、地球定向、折射消光、大气观测、轨道传播及半解析行星/月球数据支持
- 将默认未来民用时标切换为 TimeScaleLeapHour,更新 2027–2400 农历速查表、中文日期解析和时区边界处理
- 增加事件基线、跨模型回归、几何边界、月缘地形和输出契约测试,完善中英文手册、示例资源、NOTICE 与忽略规则
This commit is contained in:
2026-10-15 23:54:15 +08:00
parent 16c62a97d5
commit 044f2f4381
674 changed files with 62983 additions and 8396 deletions
+76 -64
View File
@@ -55,15 +55,17 @@ func MoonHeight(jd, lon, lat, tz float64) float64 {
return ArcSin(tmp2)
}
func HMoonAzimuth(jd, lon, lat, tz float64) float64 {
return HMoonAzimuthN(jd, lon, lat, tz, -1)
// HMoonAzimuthWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func HMoonAzimuthWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 {
return HMoonAzimuthNWithInstance(jd, lon, lat, tz, -1, inst)
}
func HMoonAzimuthN(jd, lon, lat, tz float64, n int) float64 {
// HMoonAzimuthNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func HMoonAzimuthNWithInstance(jd, lon, lat, tz float64, n int, inst *Instance) float64 {
jde := UTC2TT(jd - tz/24)
ra := HMoonTrueRaN(jde, n)
dec := HMoonTrueDecN(jde, n)
away := HMoonAwayN(jde, n) / 149597870.7
ra := HMoonTrueRaNWithInstance(jde, n, inst)
dec := HMoonTrueDecNWithInstance(jde, n, inst)
away := HMoonAwayNWithInstance(jde, n, inst) / 149597870.7
ndec := TopocentricDec(ra, dec, lat, lon, jd-tz/24, away, 0)
nra := TopocentricRa(ra, dec, lat, lon, jd-tz/24, away, 0)
jdUT := jd - tz/24
@@ -86,15 +88,9 @@ func HMoonAzimuthN(jd, lon, lat, tz float64, n int) float64 {
}
}
// HMoonHeight 当地民用时儒略日下的月心几何高度角(度,不含折射)/ geometric Moon-centre altitude in degrees for a local civil Julian day.
//
// jd 是该时区的当地民用时(墙上时刻)儒略日,tz 是时区偏移小时数,库内按 jd−tz/24 换成 UTC。
// 只有 tz 给 0 时 jd 才是 UTC 儒略日;不要拿 UTC 数值再配非零 tz,那会多减一次时区。
// jd is that zone's local civil (wall-clock) Julian day and tz is the zone offset in hours,
// converted internally as jd-tz/24. Only tz 0 makes jd a UTC Julian day: pairing a UTC value with a
// non-zero tz subtracts the offset twice.
func HMoonHeight(jd, lon, lat, tz float64) float64 {
return HMoonHeightN(jd, lon, lat, tz, -1)
// HMoonHeightWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func HMoonHeightWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 {
return HMoonHeightNWithInstance(jd, lon, lat, tz, -1, inst)
}
type moonObservationState struct {
@@ -102,10 +98,12 @@ type moonObservationState struct {
distanceKM float64
}
func hMoonObservationStateN(jd, lon, lat, tz, height float64, n int) moonObservationState {
func hMoonObservationStateN(jd, lon, lat, tz, height float64, n int, instances ...*Instance) moonObservationState {
inst := instanceValue(instances)
calculationJDE := UTC2TT(jd - tz/24)
ra, dec := HMoonTrueRaDecN(calculationJDE, n)
distanceKM := HMoonAwayN(calculationJDE, n)
ra, dec := HMoonTrueRaDecNWithInstance(calculationJDE, n, inst)
distanceKM := HMoonAwayNWithInstance(calculationJDE, n, inst)
distanceAU := distanceKM / angularDiameterAstronomicalUnitKM
topocentricRA, topocentricDec := TopocentricRaDec(ra, dec, lat, lon, jd-tz/24, distanceAU, height)
siderealTime := Limit360(ApparentSiderealTime(UTC2UT1(jd-tz/24))*15 + lon)
@@ -117,8 +115,9 @@ func hMoonObservationStateN(jd, lon, lat, tz, height float64, n int) moonObserva
}
}
func HMoonHeightN(jd, lon, lat, tz float64, n int) float64 {
return hMoonObservationStateN(jd, lon, lat, tz, 0, n).altitude
// HMoonHeightNWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func HMoonHeightNWithInstance(jd, lon, lat, tz float64, n int, inst *Instance) float64 {
return hMoonObservationStateN(jd, lon, lat, tz, 0, n, inst).altitude
}
// MoonState 同一瞬间可对任意观测点复用的月球位置与恒星时 / one instant's lunar position and sidereal time, reusable across observers.
@@ -129,14 +128,14 @@ type MoonState struct {
siderealTime float64
}
// MoonStateAt 由 UTC 儒略日构造该瞬间的可复用月球状态 / builds the reusable state for one UTC Julian day.
func MoonStateAt(utcJD float64) MoonState {
// MoonStateAtWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func MoonStateAtWithInstance(utcJD float64, inst *Instance) MoonState {
jde := UTC2TT(utcJD)
rightAscension, declination := HMoonTrueRaDec(jde)
rightAscension, declination := HMoonTrueRaDecWithInstance(jde, inst)
return MoonState{
rightAscension: rightAscension,
declination: declination,
distanceAU: HMoonAway(jde) / angularDiameterAstronomicalUnitKM,
distanceAU: HMoonAwayWithInstance(jde, inst) / angularDiameterAstronomicalUnitKM,
siderealTime: ApparentSiderealTime(UTC2UT1(utcJD)) * 15,
}
}
@@ -204,12 +203,17 @@ func (state MoonState) MoonHorizon(samples int) [][2]float64 {
return points
}
func moonRiseSetResidual(jd, longitude, latitude, timeZone, zenithShift, height float64, n int) float64 {
state := hMoonObservationStateN(jd, longitude, latitude, timeZone, height, n)
func moonRiseSetResidual(jd, longitude, latitude, timeZone, zenithShift, height float64, n int, instances ...*Instance) float64 {
return moonRiseSetResidualWeather(jd, longitude, latitude, timeZone, zenithShift, height,
refractionStandardPressureHPa, refractionStandardTemperatureC, n, instanceValue(instances))
}
func moonRiseSetResidualWeather(jd, longitude, latitude, timeZone, zenithShift, height, pressureHPa, temperatureC float64, n int, inst *Instance) float64 {
state := hMoonObservationStateN(jd, longitude, latitude, timeZone, height, n, inst)
// 相对观测者下沉地平线的视上缘高度角 / Apparent upper-limb altitude relative to the observer's depressed horizon.
residual := state.altitude + HeightDegreeByLat(height, latitude)
if zenithShift != 0 {
residual += RefractionFromTrueAltitude(state.altitude, refractionStandardPressureHPa, refractionStandardTemperatureC)
residual += RefractionFromTrueAltitude(state.altitude, pressureHPa, temperatureC)
residual += angularSemidiameterArcsec(moonEquatorialRadiusKM, state.distanceKM) / 3600
}
return residual
@@ -218,13 +222,15 @@ func moonRiseSetResidual(jd, longitude, latitude, timeZone, zenithShift, height
// moonRiseSetOnCivilDay 在民用日内求升/落时刻;找不到过零时的错误口径与 rise_set.go 的 ErrNeverRise/ErrNeverSet 一致,
// fallbackErr 是调用方用中天/下中天残差预判的同一几何结论,命中时优先于扫描结果。
func moonRiseSetOnCivilDay(candidate, slope, civilDayStart, longitude, latitude, originalTimeZone,
localTimeZone, zenithShift, height float64, isRise bool, fallbackErr error) (float64, error) {
localTimeZone, zenithShift, height float64, isRise bool, fallbackErr error, instances ...*Instance) (float64, error) {
inst := instanceValue(instances)
if eventRiseSetCandidateValid(candidate, civilDayStart, slope, isRise) {
return candidate, nil
}
return eventDirectionalRiseSetSearch(civilDayStart, isRise, fallbackErr, func(outputJD float64) float64 {
localJD := outputJD + localTimeZone/24 - originalTimeZone/24
return moonRiseSetResidual(localJD, longitude, latitude, localTimeZone, zenithShift, height, -1)
return moonRiseSetResidual(localJD, longitude, latitude, localTimeZone, zenithShift, height, -1, inst)
})
}
@@ -248,12 +254,13 @@ func GetMoonTZTime(jd, lon, lat, tz float64) float64 { //实际中天时间{
return estimateJD
}
func MoonCulminationTime(localJD, lon, lat, timezone float64) float64 {
// MoonCulminationTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func MoonCulminationTimeWithInstance(localJD, lon, lat, timezone float64, inst *Instance) float64 {
// localJD 是本地民用日锚点(当地 0 时),不是力学时;ra/dec 为瞬时天球坐标,非 J2000 等固定历元。
localJD = math.Floor(localJD) + 0.5
estimateJD := localJD + Limit360(360-MoonTimeAngle(localJD, lon, lat, timezone))/15.0/24.0/0.9
estimateJD := localJD + Limit360(360-MoonTimeAngleWithInstance(localJD, lon, lat, timezone, inst))/15.0/24.0/0.9
limitHA := func(localJD, lon, timezone float64) float64 {
ha := MoonTimeAngle(localJD, lon, lat, timezone)
ha := MoonTimeAngleWithInstance(localJD, lon, lat, timezone, inst)
if ha < 180 {
ha += 360
}
@@ -271,16 +278,18 @@ func MoonCulminationTime(localJD, lon, lat, timezone float64) float64 {
return estimateJD
}
func MoonTimeAngle(jd, lon, lat, tz float64) float64 {
// MoonTimeAngleWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func MoonTimeAngleWithInstance(jd, lon, lat, tz float64, inst *Instance) float64 {
startime := Limit360(ApparentSiderealTime(UTC2UT1(jd-tz/24))*15 + lon)
timeangle := startime - HMoonApparentRa(jd, lon, lat, tz)
timeangle := startime - HMoonApparentRaWithInstance(jd, lon, lat, tz, inst)
if timeangle < 0 {
timeangle += 360
}
return timeangle
}
func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) {
// GetMoonRiseTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func GetMoonRiseTimeWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height float64, inst *Instance) (float64, error) {
if !isFiniteFloat(julianDay) || !isFiniteFloat(longitude) || !isFiniteFloat(latitude) || !isFiniteFloat(timeZone) || !isFiniteFloat(zenithShift) || !isFiniteFloat(height) {
return 0, ErrInvalidObservationInput
}
@@ -291,11 +300,11 @@ func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, heig
// 时间分界线以传入的时区为准,不用当地时区,否则 0 时的判断会出错。
julianDay = math.Floor(julianDay) + 0.5
estimatedTime := julianDay
moonResidual := moonRiseSetResidual(julianDay, longitude, latitude, originalTimeZone, zenithShift, height, -1)
moonResidual := moonRiseSetResidual(julianDay, longitude, latitude, originalTimeZone, zenithShift, height, -1, inst)
moonAngle := StandardAltitudeMoon(zenithShift, height, latitude)
moonAngleTime := MoonTimeAngle(julianDay, longitude, latitude, originalTimeZone)
moonAngleTime := MoonTimeAngleWithInstance(julianDay, longitude, latitude, originalTimeZone, inst)
if moonResidual > 0 { // 月亮在地平线上或在落下与下中天之间
if moonAngleTime > 180 {
@@ -314,32 +323,32 @@ func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, heig
estimatedTime += (timeToMeridian/24 + (timeToMeridian/24*12.0)/15.0/24.0)
}
currentAngle := MoonTimeAngle(estimatedTime, longitude, latitude, timeZone)
currentAngle := MoonTimeAngleWithInstance(estimatedTime, longitude, latitude, timeZone, inst)
if math.Abs(currentAngle-180) > 0.5 {
estimatedTime += (180 - currentAngle) * 4.0 / 60.0 / 24.0
}
currentResidual := moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1)
currentResidual := moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1, inst)
if !(currentResidual < -10 && math.Abs(latitude) < 60) {
if currentResidual > 0 {
// 下中天仍在地平线上:当日无落下(也无可升起),口径见 moonRiseSetOnCivilDay。
return moonRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude,
originalTimeZone, timeZone, zenithShift, height, true, ErrNeverSet)
originalTimeZone, timeZone, zenithShift, height, true, ErrNeverSet, inst)
}
checkTime := estimatedTime + 12.0/24.0 + 6.0/15.0/24.0
checkAngle := MoonTimeAngle(checkTime, longitude, latitude, timeZone)
checkAngle := MoonTimeAngleWithInstance(checkTime, longitude, latitude, timeZone, inst)
if checkAngle < 90 {
checkAngle += 360
}
checkTime += (360 - checkAngle) * 4.0 / 60.0 / 24.0
if moonRiseSetResidual(checkTime, longitude, latitude, timeZone, zenithShift, height, -1) < 0 {
if moonRiseSetResidual(checkTime, longitude, latitude, timeZone, zenithShift, height, -1, inst) < 0 {
// 上中天仍在地平线下:当日无升起。
return moonRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude,
originalTimeZone, timeZone, zenithShift, height, true, ErrNeverRise)
originalTimeZone, timeZone, zenithShift, height, true, ErrNeverRise, inst)
}
}
moonDeclination := MoonApparentDec(estimatedTime, longitude, latitude, timeZone)
moonDeclination := MoonApparentDecWithInstance(estimatedTime, longitude, latitude, timeZone, inst)
tmp := (Sin(moonAngle) - Sin(moonDeclination)*Sin(latitude)) / (Cos(moonDeclination) * Cos(latitude))
if math.Abs(tmp) <= 1 && latitude < 85 {
@@ -347,7 +356,7 @@ func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, heig
estimatedTime += hourAngle/24.00 + hourAngle/33.00/15.00
} else {
i := 0
for moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1) < 0 {
for moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1, inst) < 0 {
i++
estimatedTime += 15.0 / 60.0 / 24.0
if i > 48 {
@@ -357,13 +366,14 @@ func GetMoonRiseTime(julianDay, longitude, latitude, timeZone, zenithShift, heig
}
// 使用牛顿迭代法求精确解
estimatedTime, slope := moonRiseSetResidualIteration(estimatedTime, longitude, latitude, timeZone, zenithShift, height, 0.00002)
estimatedTime, slope := moonRiseSetResidualIteration(estimatedTime, longitude, latitude, timeZone, zenithShift, height, 0.00002, inst)
estimatedTime = estimatedTime - timeZone/24 + originalTimeZone/24
return moonRiseSetOnCivilDay(estimatedTime, slope, civilDayStart, longitude, latitude,
originalTimeZone, timeZone, zenithShift, height, true, nil)
originalTimeZone, timeZone, zenithShift, height, true, nil, inst)
}
func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, height float64) (float64, error) {
// GetMoonSetTimeWithInstance 用指定星历实例求值;inst 为 nil 时用进程当前模型 / evaluation with an explicit instance; nil uses the process model.
func GetMoonSetTimeWithInstance(julianDay, longitude, latitude, timeZone, zenithShift, height float64, inst *Instance) (float64, error) {
if !isFiniteFloat(julianDay) || !isFiniteFloat(longitude) || !isFiniteFloat(latitude) || !isFiniteFloat(timeZone) || !isFiniteFloat(zenithShift) || !isFiniteFloat(height) {
return 0, ErrInvalidObservationInput
}
@@ -374,11 +384,11 @@ func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, heigh
// 时间分界线以传入的时区为准,不用当地时区,否则 0 时的判断会出错。
julianDay = math.Floor(julianDay) + 0.5
estimatedTime := julianDay
moonResidual := moonRiseSetResidual(julianDay, longitude, latitude, originalTimeZone, zenithShift, height, -1)
moonResidual := moonRiseSetResidual(julianDay, longitude, latitude, originalTimeZone, zenithShift, height, -1, inst)
moonAngle := StandardAltitudeMoon(zenithShift, height, latitude)
moonAngleTime := MoonTimeAngle(julianDay, longitude, latitude, originalTimeZone)
moonAngleTime := MoonTimeAngleWithInstance(julianDay, longitude, latitude, originalTimeZone, inst)
if moonResidual < 0 {
timeToMeridian = (360 - moonAngleTime) / 15
@@ -394,7 +404,7 @@ func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, heigh
estimatedTime += (timeToMeridian/24.0 + (timeToMeridian/24.0*12.0)/15.0/24.0)
}
currentAngle := MoonTimeAngle(estimatedTime, longitude, latitude, timeZone)
currentAngle := MoonTimeAngleWithInstance(estimatedTime, longitude, latitude, timeZone, inst)
if currentAngle < 180 {
currentAngle += 360
}
@@ -403,24 +413,24 @@ func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, heigh
}
// estimatedTime = 月球中天时间
currentResidual := moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1)
currentResidual := moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1, inst)
if !(currentResidual > 10 && math.Abs(latitude) < 60) {
if currentResidual < 0 {
// 上中天仍在地平线下:当日无升起,也就无落下。
return moonRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude,
originalTimeZone, timeZone, zenithShift, height, false, ErrNeverRise)
originalTimeZone, timeZone, zenithShift, height, false, ErrNeverRise, inst)
}
checkTime := estimatedTime + 12.0/24.0 + 6.0/15.0/24.0
angleSubtraction := 180 - MoonTimeAngle(checkTime, longitude, latitude, timeZone)
angleSubtraction := 180 - MoonTimeAngleWithInstance(checkTime, longitude, latitude, timeZone, inst)
checkTime += angleSubtraction * 4.0 / 60.0 / 24.0
if moonRiseSetResidual(checkTime, longitude, latitude, timeZone, zenithShift, height, -1) > 0 {
if moonRiseSetResidual(checkTime, longitude, latitude, timeZone, zenithShift, height, -1, inst) > 0 {
// 下中天仍在地平线上:当日无落下。
return moonRiseSetOnCivilDay(math.NaN(), math.NaN(), civilDayStart, longitude, latitude,
originalTimeZone, timeZone, zenithShift, height, false, ErrNeverSet)
originalTimeZone, timeZone, zenithShift, height, false, ErrNeverSet, inst)
}
}
moonDeclination := MoonApparentDec(estimatedTime, longitude, latitude, timeZone)
moonDeclination := MoonApparentDecWithInstance(estimatedTime, longitude, latitude, timeZone, inst)
tmp := (Sin(moonAngle) - Sin(moonDeclination)*Sin(latitude)) / (Cos(moonDeclination) * Cos(latitude))
if math.Abs(tmp) <= 1 && latitude < 85 {
@@ -428,7 +438,7 @@ func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, heigh
estimatedTime += hourAngle/24 + hourAngle/33.0/15.0
} else {
i := 0
for moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1) > 0 {
for moonRiseSetResidual(estimatedTime, longitude, latitude, timeZone, zenithShift, height, -1, inst) > 0 {
i++
estimatedTime += 15.0 / 60.0 / 24.0
if i > 48 {
@@ -438,10 +448,10 @@ func GetMoonSetTime(julianDay, longitude, latitude, timeZone, zenithShift, heigh
}
// 使用牛顿迭代法求精确解
estimatedTime, slope := moonRiseSetResidualIteration(estimatedTime, longitude, latitude, timeZone, zenithShift, height, 0.00002)
estimatedTime, slope := moonRiseSetResidualIteration(estimatedTime, longitude, latitude, timeZone, zenithShift, height, 0.00002, inst)
estimatedTime = estimatedTime - timeZone/24 + originalTimeZone/24
return moonRiseSetOnCivilDay(estimatedTime, slope, civilDayStart, longitude, latitude,
originalTimeZone, timeZone, zenithShift, height, false, nil)
originalTimeZone, timeZone, zenithShift, height, false, nil, inst)
}
// heightFunction 高度函数类型定义,用于牛顿迭代法
@@ -468,14 +478,16 @@ func moonRiseSetNewtonRaphsonIteration(initialTime, longitude, latitude, timeZon
return currentTime
}
func moonRiseSetResidualIteration(initialTime, longitude, latitude, timeZone, zenithShift, height, tolerance float64) (float64, float64) {
func moonRiseSetResidualIteration(initialTime, longitude, latitude, timeZone, zenithShift, height, tolerance float64, instances ...*Instance) (float64, float64) {
inst := instanceValue(instances)
const derivativeStep = 0.000005
slope := math.NaN()
currentTime, ok := eventNewtonRefine(initialTime, tolerance, func(previousTime float64) float64 {
functionValue := moonRiseSetResidual(previousTime, longitude, latitude, timeZone, zenithShift, height, -1)
slope = (moonRiseSetResidual(previousTime+derivativeStep, longitude, latitude, timeZone, zenithShift, height, -1) -
moonRiseSetResidual(previousTime-derivativeStep, longitude, latitude, timeZone, zenithShift, height, -1)) / (2 * derivativeStep)
functionValue := moonRiseSetResidual(previousTime, longitude, latitude, timeZone, zenithShift, height, -1, inst)
slope = (moonRiseSetResidual(previousTime+derivativeStep, longitude, latitude, timeZone, zenithShift, height, -1, inst) -
moonRiseSetResidual(previousTime-derivativeStep, longitude, latitude, timeZone, zenithShift, height, -1, inst)) / (2 * derivativeStep)
return functionValue / slope
})
if !ok {