Skip to content

Commit

Permalink
Merge pull request #990 from thkruz/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
thkruz authored Dec 11, 2024
2 parents 0e9a6ec + 57ed3ce commit efc012b
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 47 deletions.
2 changes: 1 addition & 1 deletion src/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export interface SensorObjectCruncher {
}

export type lookanglesRow = {
sortTime: number;
START_DTG: number | string;
SATELLITE_ID: string;
PASS_SCORE: string;
START_DATE: Date | string
Expand Down
105 changes: 97 additions & 8 deletions src/plugins/analysis/analysis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import { saveCsv } from '@app/lib/saveVariable';
import { CatalogExporter } from '@app/static/catalog-exporter';
import { CatalogSearch } from '@app/static/catalog-search';
import analysisPng from '@public/img/icons/analysis.png';
import { DetailedSatellite, DetailedSensor, eci2rae, EciVec3, Kilometers, MINUTES_PER_DAY, SatelliteRecord, TAU } from 'ootk';
import { DetailedSatellite, DetailedSensor, eci2rae, EciVec3, Kilometers, MILLISECONDS_PER_SECOND, MINUTES_PER_DAY, SatelliteRecord, TAU } from 'ootk';
import { KeepTrackPlugin } from '../KeepTrackPlugin';
import { WatchlistPlugin } from '../watchlist/watchlist';

/**
* /*! /////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -164,6 +165,43 @@ export class AnalysisMenu extends KeepTrackPlugin {
</div>
</form>
</div>
<h5 class="center-align">Satellite Overflight</h5>
<div class="divider"></div>
<div class="row"></div>
<div class="row">
<form id="analysis-overflight">
<div class="row">
<div class="input-field col s12">
<input value="41.888935617165025" id="analysis-of-lat" type="text" />
<label for="analysis-of-lat" class="active">Latitude</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input value="2" id="analysis-of-lat-marg" type="text" />
<label for="analysis-of-lat-marg" class="active">Latitude Margin</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input value="12.484747346796043" id="analysis-of-lon" type="text" />
<label for="analysis-of-lon" class="active">Longitude</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input value="3" id="analysis-of-lon-marg" type="text" />
<label for="analysis-of-lon-marg" class="active">Longitude Margin</label>
</div>
</div>
<div class="row">
<center>
<button id="analysis-overflight-submit" class="btn btn-ui waves-effect waves-light" type="submit"
name="action">Generate Overflight Times &#9658;</button>
</center>
</div>
</form>
</div>
</div>
</div>
`;
Expand All @@ -180,6 +218,11 @@ export class AnalysisMenu extends KeepTrackPlugin {
AnalysisMenu.analysisBptSumbit_();
});

getEl('analysis-overflight')?.addEventListener('submit', (e: Event) => {
e.preventDefault();
AnalysisMenu.findOverflight_();
});

getEl('findCsoBtn')?.addEventListener('click', () => {
showLoading(this.findCsoBtnClick_.bind(this));
});
Expand Down Expand Up @@ -471,7 +514,7 @@ export class AnalysisMenu extends KeepTrackPlugin {
// Skip pass if satellite is in track right now
if (sTime === null) {
return {
sortTime: null,
START_DTG: null,
SATELLITE_ID: null,
PASS_SCORE: null,
START_DATE: null,
Expand Down Expand Up @@ -514,7 +557,7 @@ export class AnalysisMenu extends KeepTrackPlugin {

// Last Line of Coverage
return {
sortTime: sTime.getTime(),
START_DTG: sTime.getTime(),
SATELLITE_ID: parseInt(satrecIn.satnum).toString(),
PASS_SCORE: score.toFixed(1),
START_DATE: sTime,
Expand Down Expand Up @@ -545,7 +588,7 @@ export class AnalysisMenu extends KeepTrackPlugin {
}

return {
sortTime: null,
START_DTG: null,
SATELLITE_ID: null,
PASS_SCORE: null,
START_DATE: null,
Expand Down Expand Up @@ -618,13 +661,11 @@ export class AnalysisMenu extends KeepTrackPlugin {
console.debug(e);
}
}
passes.sort((a, b) => b.sortTime - a.sortTime);
passes.sort((a, b) => (b.START_DTG as number) - (a.START_DTG as number));
passes.reverse();
passes.forEach((v) => {
delete v.sortTime;
});

for (const pass of passes) {
pass.START_DTG = (<Date>pass.START_DATE).toISOString();
pass.START_DATE = (<Date>pass.START_DATE).toISOString().split('T')[0];
pass.START_TIME = (<Date>pass.START_TIME).toISOString().split('T')[1].split('.')[0];
pass.STOP_DATE = (<Date>pass.STOP_DATE).toISOString().split('T')[0];
Expand Down Expand Up @@ -657,6 +698,54 @@ export class AnalysisMenu extends KeepTrackPlugin {
}
}

private static findOverflight_() {
// Get lat, lon, lat margin, and lon margin
const lat = parseFloat((<HTMLInputElement>getEl('analysis-of-lat')).value);
const lon = parseFloat((<HTMLInputElement>getEl('analysis-of-lon')).value);
const latMargin = parseFloat((<HTMLInputElement>getEl('analysis-of-lat-marg')).value);
const lonMargin = parseFloat((<HTMLInputElement>getEl('analysis-of-lon-marg')).value);

// Get watchlist satellites
const idList = keepTrackApi.getPlugin(WatchlistPlugin).getSatellites();

// For each satellite loop through 72 hours at 30 second intervals
const durationInSeconds = 72 * 60 * 60;
const overflights = [];

for (const satId of idList) {
const sat = keepTrackApi.getCatalogManager().getSat(satId);
let time = this.getStartTime_();

for (let t = 0; t < durationInSeconds; t += 30) {
time = new Date(time.getTime() + 30 * MILLISECONDS_PER_SECOND);
const lla = sat.lla(time);

if (lla.lat > lat - latMargin && lla.lat < lat + latMargin && lla.lon > lon - lonMargin && lla.lon < lon + lonMargin) {
overflights.push({
START_DTG: time.toISOString(),
SATELLITE_ID: sat.sccNum,
LATITUDE: lla.lat,
LONGITUDE: lla.lon,
});
}
}
}

// sort overflights by time
overflights.sort((a, b) => new Date(a.START_DTG).getTime() - new Date(b.START_DTG).getTime());

saveCsv(overflights, 'overflights');
}

private static getStartTime_() {
const time = keepTrackApi.getTimeManager().getOffsetTimeObj(0);

time.setMilliseconds(0);
time.setSeconds(0);

return time;
}

private static setSensor_(sensor: DetailedSensor | string): void {
const submitButtonDom = <HTMLButtonElement>getEl('analysis-bpt-submit');

Expand Down
5 changes: 4 additions & 1 deletion src/plugins/watchlist/watchlist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,9 @@ export class WatchlistPlugin extends KeepTrackPlugin {
return this.watchlistList.some(({ id: id_ }) => id_ === id);
}

/**
* @returns An array of satellite ids in the watchlist.
*/
getSatellites() {
return this.watchlistList.map(({
id,
Expand All @@ -384,7 +387,7 @@ export class WatchlistPlugin extends KeepTrackPlugin {
*/
private onAddEvent_() {
keepTrackApi.getSoundManager().play(SoundNames.CLICK);
const sats = (<HTMLInputElement>getEl('watchlist-new')).value.split(',');
const sats = (<HTMLInputElement>getEl('watchlist-new')).value.split(/[\s,]+/u);

sats.forEach((satNum: string) => {
const id = keepTrackApi.getCatalogManager().sccNum2Id(parseInt(satNum));
Expand Down
2 changes: 1 addition & 1 deletion src/settings/versionDate.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
export const VERSION_DATE = 'October 29, 2024';
export const VERSION_DATE = 'November 7, 2024';
56 changes: 48 additions & 8 deletions src/static/catalog-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/

import { BaseObject, CatalogSource, Degrees, DetailedSatellite, Minutes, SpaceObjectType } from 'ootk';
import { SatMath } from './sat-math';

/**
* The CatalogSearch class provides static methods for filtering and searching through an array of satellite data.
Expand Down Expand Up @@ -112,30 +113,69 @@ export class CatalogSearch {
minRaan += 360;
}

const now = new Date();
const normalizedSatRaan = this.normalizeRaan(sat, now);

return satData
.filter((s) => {
// Skip static objects
if (s.isStatic()) {
return false;
}

// Check inclination bounds
if (s.inclination < minInclination || s.inclination > maxInclination) {
return false;
}
if (sat.rightAscension > 360 - RAAN_MARGIN || sat.rightAscension < RAAN_MARGIN) {
if (s.rightAscension > maxRaan && s.rightAscension < minRaan) {
return false;
}
} else if (s.rightAscension < minRaan || s.rightAscension > maxRaan) {
return false;
}

// Check period bounds
if (s.period < minPeriod || s.period > maxPeriod) {
return false;
}

return true;
const normalizedSearchRaan = this.normalizeRaan(s, now);

// Handle RAAN wraparound case
if (normalizedSatRaan > 360 - RAAN_MARGIN || normalizedSatRaan < RAAN_MARGIN) {
return normalizedSearchRaan > minRaan || normalizedSearchRaan < maxRaan;
}

// Check RAAN bounds (normal case)
return !(normalizedSearchRaan < minRaan || normalizedSearchRaan > maxRaan);
})
.map((s) => s.id);
}

// Normalize the RAAN based on nodal precession
static normalizeRaan(sat: DetailedSatellite, now: Date): number {
const precessionRate = this.getNodalPrecessionRate(sat);
const daysSinceEpoch = SatMath.calcElsetAge(sat, now);
let normalizedRaan = sat.rightAscension + (precessionRate * daysSinceEpoch);

// Ensure RAAN stays within 0-360 range
normalizedRaan = ((normalizedRaan % 360) + 360) % 360;

return normalizedRaan;
}

// Calculate nodal precession rate (degrees per day)
static getNodalPrecessionRate(s: DetailedSatellite): number {
const Re = 6378137; // Earth radius in meters
const J2 = 1.082626680e-3; // Earth's second dynamic form factor
const period = s.period * 60; // Convert period from minutes to seconds
const omega = (2 * Math.PI) / period; // Angular velocity in rad/s
const a = s.semiMajorAxis * 1000; // Convert semi-major axis from km to meters
const e = s.eccentricity;
const i = s.inclination * Math.PI / 180; // Convert inclination to radians

// Calculate precession rate in rad/s
const omegaP = (-3 / 2) * (Re / a) ** 2 / (1 - e * e) ** 2 * J2 * omega * Math.cos(i);

// Convert to degrees per day
return omegaP * (180 / Math.PI) * 86400;
}


/**
* This method is used to find the reentry objects from the given satellite data.
* It filters the satellite data based on the type of the object (PAYLOAD, ROCKET_BODY, DEBRIS) and the perigee value.
Expand Down
49 changes: 21 additions & 28 deletions src/static/classification.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,32 @@ export class Classification {
let backgroundColor: string;
let color: string;

switch (classification) {
case 'Top Secret//SCI':
backgroundColor = '#fce93a';
color = 'black';
break;
case 'Top Secret':
backgroundColor = '#ff8c00';
color = 'black';
break;
case 'Secret':
backgroundColor = '#ff0000';
color = 'white';
break;
case 'Confidential':
backgroundColor = '#0033a0';
color = 'white';
break;
case 'CUI':
backgroundColor = '#512b85';
color = 'white';
break;
case 'Unclassified':
backgroundColor = '#007a33';
color = 'white';
break;
default:
throw new Error(`Invalid classification: ${classification}`);
if (classification.startsWith('Top Secret//SCI')) {
backgroundColor = '#fce93a';
color = 'black';
} else if (classification.startsWith('Top Secret')) {
backgroundColor = '#ff8c00';
color = 'black';
} else if (classification.startsWith('Secret')) {
backgroundColor = '#ff0000';
color = 'white';
} else if (classification.startsWith('Confidential')) {
backgroundColor = '#0033a0';
color = 'white';
} else if (classification.startsWith('CUI')) {
backgroundColor = '#512b85';
color = 'white';
} else if (classification.startsWith('Unclassified')) {
backgroundColor = '#007a33';
color = 'white';
} else {
throw new Error(`Invalid classification: ${classification}`);
}

return { backgroundColor, color };
}

static isValidClassification(classification: string): boolean {
return ['Unclassified', 'Confidential', 'CUI', 'Secret', 'Top Secret', 'Top Secret//SCI'].includes(classification);
return ['Unclassified', 'Confidential', 'CUI', 'Secret', 'Top Secret', 'Top Secret//SCI'].some((validClassification) => classification.startsWith(validClassification));
}
}

0 comments on commit efc012b

Please sign in to comment.