sysml2kit-rf-library 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sysml2kit_rf_library/__init__.py +40 -0
- sysml2kit_rf_library/_build.py +358 -0
- sysml2kit_rf_library/_version.py +24 -0
- sysml2kit_rf_library/models/RFAnalyses.sysml +15 -0
- sysml2kit_rf_library/models/RFParts.sysml +36 -0
- sysml2kit_rf_library/models/RFRequirements.sysml +33 -0
- sysml2kit_rf_library/models/RFVocabulary.sysml +42 -0
- sysml2kit_rf_library/models/SatcomTerminal28GHz.sysml +109 -0
- sysml2kit_rf_library/models/interchange/rf_library.json +380 -0
- sysml2kit_rf_library/models/interchange/satcom_terminal_t3001.json +1274 -0
- sysml2kit_rf_library/py.typed +0 -0
- sysml2kit_rf_library-0.1.0.dist-info/METADATA +104 -0
- sysml2kit_rf_library-0.1.0.dist-info/RECORD +16 -0
- sysml2kit_rf_library-0.1.0.dist-info/WHEEL +4 -0
- sysml2kit_rf_library-0.1.0.dist-info/licenses/LICENSE +202 -0
- sysml2kit_rf_library-0.1.0.dist-info/licenses/NOTICE +10 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""SysML v2 model library for antenna/RF systems engineering.
|
|
2
|
+
|
|
3
|
+
Ships the RFVocabulary/RFParts/RFRequirements/RFAnalyses packages plus a
|
|
4
|
+
worked 28 GHz satcom-terminal example, as committed interchange JSON loadable
|
|
5
|
+
through sysml2kit. ``_build.py`` is the authoritative source; the files under
|
|
6
|
+
``models/`` are generated from it.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
from sysml2kit.model import Model
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
__version__ = version("sysml2kit-rf-library")
|
|
16
|
+
except PackageNotFoundError: # running from a source tree without an install
|
|
17
|
+
__version__ = "0.0.0.dev0"
|
|
18
|
+
|
|
19
|
+
#: Available model names -> interchange file stem.
|
|
20
|
+
MODELS = {
|
|
21
|
+
"rf-library": "rf_library",
|
|
22
|
+
"satcom-terminal-t3001": "satcom_terminal_t3001",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def models_dir() -> Path:
|
|
27
|
+
"""Return the directory holding the packaged model files."""
|
|
28
|
+
return Path(__file__).parent / "models"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def load_model(name: str = "rf-library") -> Model:
|
|
32
|
+
"""Load a packaged model by name (see ``MODELS``)."""
|
|
33
|
+
from sysml2kit.interchange import model_from_json
|
|
34
|
+
|
|
35
|
+
if name not in MODELS:
|
|
36
|
+
raise KeyError(f"unknown model {name!r}; available: {sorted(MODELS)}")
|
|
37
|
+
return model_from_json(models_dir() / "interchange" / f"{MODELS[name]}.json")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
__all__ = ["MODELS", "__version__", "load_model", "models_dir"]
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
"""Authoritative construction of the RF model library.
|
|
2
|
+
|
|
3
|
+
This module is the source of truth. The committed ``.sysml`` files and
|
|
4
|
+
interchange JSON under ``models/`` are generated from it by
|
|
5
|
+
``scripts/regenerate.py`` (CI checks they are in sync). Building with the
|
|
6
|
+
sysml2kit builder keeps full fidelity — attribute values, units, and
|
|
7
|
+
relationship kinds — which a text parse cannot yet guarantee.
|
|
8
|
+
|
|
9
|
+
The example terminal mirrors the aedl ``t3-001`` benchmark: a 28 GHz LEO
|
|
10
|
+
uplink phased-array ground terminal with its published requirement set.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from sysml2kit.model import Element, Model, builder
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def build_vocabulary(model: Model) -> Element:
|
|
19
|
+
"""RFVocabulary: attribute definitions with canonical units."""
|
|
20
|
+
pkg = builder.pkg(
|
|
21
|
+
model,
|
|
22
|
+
"RFVocabulary",
|
|
23
|
+
doc="Quantity kinds for antenna/RF systems engineering, with canonical units.",
|
|
24
|
+
)
|
|
25
|
+
quantities = [
|
|
26
|
+
("Frequency_GHz", "GHz", "Carrier or design frequency."),
|
|
27
|
+
("Bandwidth_MHz", "MHz", "Occupied or instantaneous bandwidth."),
|
|
28
|
+
("Gain_dBi", "dBi", "Antenna gain relative to isotropic."),
|
|
29
|
+
("EIRP_dBW", "dBW", "Effective isotropic radiated power."),
|
|
30
|
+
("GOverT_dBK", "dBK", "Receive figure of merit G/T."),
|
|
31
|
+
("SidelobeLevel_dB", "dB", "Peak sidelobe level relative to the main beam."),
|
|
32
|
+
("ScanLoss_dB", "dB", "Gain loss at the scan angle relative to broadside."),
|
|
33
|
+
("LinkMargin_dB", "dB", "Margin above the required SNR."),
|
|
34
|
+
("NoiseFigure_dB", "dB", "Cascaded receive noise figure."),
|
|
35
|
+
("AxialRatio_dB", "dB", "Polarization axial ratio."),
|
|
36
|
+
("ScanAngle_deg", "deg", "Beam scan angle off broadside."),
|
|
37
|
+
("PrimePower_W", "W", "Prime (DC input) power draw."),
|
|
38
|
+
("UnitCost_USD", None, "Recurring unit cost in US dollars."),
|
|
39
|
+
]
|
|
40
|
+
for name, unit, doc in quantities:
|
|
41
|
+
builder.attr_def(model, name, owner=pkg, unit=unit, doc=doc)
|
|
42
|
+
return pkg
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def build_parts(model: Model) -> Element:
|
|
46
|
+
"""RFParts: part and port definitions for phased-array terminals."""
|
|
47
|
+
pkg = builder.pkg(
|
|
48
|
+
model,
|
|
49
|
+
"RFParts",
|
|
50
|
+
doc="Part and port definitions for phased-array antenna systems.",
|
|
51
|
+
)
|
|
52
|
+
builder.port_def(model, "RFPort", owner=pkg, doc="Guided-wave RF interface.")
|
|
53
|
+
builder.port_def(model, "BeamPort", owner=pkg, doc="Formed-beam signal interface.")
|
|
54
|
+
builder.port_def(model, "CtrlPort", owner=pkg, doc="Command and telemetry interface.")
|
|
55
|
+
parts = [
|
|
56
|
+
("AntennaElement", "A single radiating element."),
|
|
57
|
+
("RadiatingAperture", "The element lattice as one aperture."),
|
|
58
|
+
("TRModule", "Transmit/receive module: PA, LNA, phase shifter, switch."),
|
|
59
|
+
("Beamformer", "Analog, digital, or hybrid beamforming network."),
|
|
60
|
+
("PhasedArrayAntenna", "Aperture, T/R modules, and beamformer as one antenna."),
|
|
61
|
+
("RFFrontEnd", "Up/down conversion and filtering between antenna and modem."),
|
|
62
|
+
("Modem", "Waveform modulation and demodulation."),
|
|
63
|
+
("SatcomTerminal", "A complete ground terminal."),
|
|
64
|
+
]
|
|
65
|
+
for name, doc in parts:
|
|
66
|
+
builder.part_def(model, name, owner=pkg, doc=doc)
|
|
67
|
+
return pkg
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def build_requirement_defs(model: Model) -> Element:
|
|
71
|
+
"""RFRequirements: requirement definitions carrying the metricKey convention.
|
|
72
|
+
|
|
73
|
+
Each definition documents which ``metricKey`` its usages set, so
|
|
74
|
+
``sysml2kit.interop.extract_requirements`` can hand them to a requirements
|
|
75
|
+
engine (phased-array-systems, aedl) without RF knowledge in the kit.
|
|
76
|
+
"""
|
|
77
|
+
pkg = builder.pkg(
|
|
78
|
+
model,
|
|
79
|
+
"RFRequirements",
|
|
80
|
+
doc=(
|
|
81
|
+
"Requirement definitions for RF terminals. Usages own attributes "
|
|
82
|
+
"metricKey (str), threshold (number with unit), op (>=, <=, ==), and "
|
|
83
|
+
"optionally severity (must/should/nice); sysml2kit.interop reads them."
|
|
84
|
+
),
|
|
85
|
+
)
|
|
86
|
+
defs = [
|
|
87
|
+
("EirpRequirement", "Minimum effective isotropic radiated power."),
|
|
88
|
+
("GOverTRequirement", "Minimum receive figure of merit."),
|
|
89
|
+
("SidelobeRequirement", "Maximum pattern sidelobe level."),
|
|
90
|
+
("ScanLossRequirement", "Maximum gain loss at the scan-angle extreme."),
|
|
91
|
+
("LinkMarginRequirement", "Minimum worst-case link margin."),
|
|
92
|
+
("BandwidthRequirement", "Minimum instantaneous bandwidth."),
|
|
93
|
+
("PowerCeilingRequirement", "Maximum prime-power draw."),
|
|
94
|
+
("CostCeilingRequirement", "Maximum recurring unit cost."),
|
|
95
|
+
("CrosscheckRequirement", "Maximum disagreement between independent analyses."),
|
|
96
|
+
("GratingLobeRequirement", "Minimum grating-lobe margin in wavelengths."),
|
|
97
|
+
]
|
|
98
|
+
for name, doc in defs:
|
|
99
|
+
builder.req_def(model, name, owner=pkg, doc=doc)
|
|
100
|
+
return pkg
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def build_analyses(model: Model) -> Element:
|
|
104
|
+
"""RFAnalyses: analysis case definitions that verification links point at."""
|
|
105
|
+
pkg = builder.pkg(
|
|
106
|
+
model,
|
|
107
|
+
"RFAnalyses",
|
|
108
|
+
doc="Analysis case definitions; bindings to physics engines live downstream.",
|
|
109
|
+
)
|
|
110
|
+
cases = [
|
|
111
|
+
("LinkBudgetAnalysis", "Worst-case link closure over an evaluation envelope."),
|
|
112
|
+
("ArrayPatternAnalysis", "Full pattern integration: gain, sidelobes, grating lobes."),
|
|
113
|
+
("ScanPerformanceAnalysis", "Gain and pattern degradation across the scan envelope."),
|
|
114
|
+
("SwapCostAnalysis", "Prime power and recurring cost rollup."),
|
|
115
|
+
]
|
|
116
|
+
for name, doc in cases:
|
|
117
|
+
builder.analysis_def(model, name, owner=pkg, doc=doc)
|
|
118
|
+
return pkg
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def build_library() -> Model:
|
|
122
|
+
"""Build the four library packages in one model."""
|
|
123
|
+
model = Model()
|
|
124
|
+
build_vocabulary(model)
|
|
125
|
+
build_parts(model)
|
|
126
|
+
build_requirement_defs(model)
|
|
127
|
+
build_analyses(model)
|
|
128
|
+
return model
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _metric_requirement(
|
|
132
|
+
model: Model,
|
|
133
|
+
owner: Element,
|
|
134
|
+
short: str,
|
|
135
|
+
name: str,
|
|
136
|
+
*,
|
|
137
|
+
definition: Element,
|
|
138
|
+
text: str,
|
|
139
|
+
metric_key: str,
|
|
140
|
+
op: str,
|
|
141
|
+
threshold: float,
|
|
142
|
+
unit: str | None,
|
|
143
|
+
severity: str = "must",
|
|
144
|
+
) -> Element:
|
|
145
|
+
req = builder.req(model, short, name, owner=owner, text=text, definition=definition)
|
|
146
|
+
builder.attr(model, "metricKey", metric_key, owner=req)
|
|
147
|
+
builder.attr(model, "threshold", threshold, owner=req, unit=unit)
|
|
148
|
+
builder.attr(model, "op", op, owner=req)
|
|
149
|
+
builder.attr(model, "severity", severity, owner=req)
|
|
150
|
+
return req
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _defs(model: Model) -> dict[str, Element]:
|
|
154
|
+
return {el.declared_name: el for el in model.iter_elements() if el.declared_name}
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _add_example(model: Model) -> Model:
|
|
158
|
+
defs = _defs(model)
|
|
159
|
+
pkg = builder.pkg(
|
|
160
|
+
model,
|
|
161
|
+
"SatcomTerminal28GHz",
|
|
162
|
+
doc=(
|
|
163
|
+
"28 GHz LEO uplink phased-array ground terminal, mirroring the aedl "
|
|
164
|
+
"t3-001 benchmark: close the worst-case link over the evaluation "
|
|
165
|
+
"envelope inside a 450 W prime-power and 45 kUSD unit-cost ceiling."
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
pkg.imports = ["RFVocabulary::*", "RFParts::*", "RFRequirements::*", "RFAnalyses::*"]
|
|
169
|
+
|
|
170
|
+
terminal = builder.part(model, "terminal", owner=pkg, definition=defs["SatcomTerminal"])
|
|
171
|
+
array = builder.part(model, "array", owner=terminal, definition=defs["PhasedArrayAntenna"])
|
|
172
|
+
builder.attr(model, "frequency", 28.0, owner=array, unit="GHz", source="t3-001 brief")
|
|
173
|
+
builder.attr(model, "bandwidth", 50.0, owner=array, unit="MHz", source="t3-001 brief")
|
|
174
|
+
builder.attr(model, "maxScanAngle", 60.0, owner=array, unit="deg", source="t3-001 envelope")
|
|
175
|
+
builder.part(
|
|
176
|
+
model,
|
|
177
|
+
"elements",
|
|
178
|
+
owner=array,
|
|
179
|
+
definition=defs["AntennaElement"],
|
|
180
|
+
multiplicity="[1..*]",
|
|
181
|
+
)
|
|
182
|
+
builder.part(
|
|
183
|
+
model, "trModules", owner=array, definition=defs["TRModule"], multiplicity="[1..*]"
|
|
184
|
+
)
|
|
185
|
+
beamformer = builder.part(model, "beamformer", owner=array, definition=defs["Beamformer"])
|
|
186
|
+
front_end = builder.part(model, "frontEnd", owner=terminal, definition=defs["RFFrontEnd"])
|
|
187
|
+
builder.part(model, "modem", owner=terminal, definition=defs["Modem"])
|
|
188
|
+
beam_out = builder.port(model, "beamOut", owner=beamformer, definition=defs["BeamPort"])
|
|
189
|
+
fe_in = builder.port(model, "beamIn", owner=front_end, definition=defs["BeamPort"])
|
|
190
|
+
builder.connect(model, beam_out, fe_in, owner=terminal, name="beamFeed")
|
|
191
|
+
|
|
192
|
+
reqs = [
|
|
193
|
+
(
|
|
194
|
+
"REQ-LINK-MARGIN",
|
|
195
|
+
"WorstCaseLinkMargin",
|
|
196
|
+
defs["LinkMarginRequirement"],
|
|
197
|
+
"The link shall close (margin >= 0 dB) worst-case over the evaluation envelope.",
|
|
198
|
+
"worst_case_link_margin_db",
|
|
199
|
+
">=",
|
|
200
|
+
0.0,
|
|
201
|
+
"dB",
|
|
202
|
+
"must",
|
|
203
|
+
),
|
|
204
|
+
(
|
|
205
|
+
"REQ-SLL",
|
|
206
|
+
"PatternSidelobes",
|
|
207
|
+
defs["SidelobeRequirement"],
|
|
208
|
+
"Worst-case pattern sidelobe level shall not exceed -16 dB.",
|
|
209
|
+
"worst_case_pattern_sll_db",
|
|
210
|
+
"<=",
|
|
211
|
+
-16.0,
|
|
212
|
+
"dB",
|
|
213
|
+
"must",
|
|
214
|
+
),
|
|
215
|
+
(
|
|
216
|
+
"REQ-INDEP-LINK",
|
|
217
|
+
"IndependentLinkClosure",
|
|
218
|
+
defs["LinkMarginRequirement"],
|
|
219
|
+
"The link shall also close under the independent opensatcom recomputation.",
|
|
220
|
+
"opensatcom_worst_case_margin_db",
|
|
221
|
+
">=",
|
|
222
|
+
0.0,
|
|
223
|
+
"dB",
|
|
224
|
+
"must",
|
|
225
|
+
),
|
|
226
|
+
(
|
|
227
|
+
"REQ-CLEARSKY-AGREE",
|
|
228
|
+
"ClearSkyAgreement",
|
|
229
|
+
defs["CrosscheckRequirement"],
|
|
230
|
+
"Clear-sky margins from the two link analyses shall agree within 1.2 dB.",
|
|
231
|
+
"crosscheck_clearsky_margin_disagreement_db",
|
|
232
|
+
"<=",
|
|
233
|
+
1.2,
|
|
234
|
+
"dB",
|
|
235
|
+
"should",
|
|
236
|
+
),
|
|
237
|
+
(
|
|
238
|
+
"REQ-GAIN-XCHECK",
|
|
239
|
+
"GainCrosscheck",
|
|
240
|
+
defs["CrosscheckRequirement"],
|
|
241
|
+
"Claimed and integrated gain shall agree within 0.5 dB.",
|
|
242
|
+
"crosscheck_gain_disagreement_db",
|
|
243
|
+
"<=",
|
|
244
|
+
0.5,
|
|
245
|
+
"dB",
|
|
246
|
+
"should",
|
|
247
|
+
),
|
|
248
|
+
(
|
|
249
|
+
"REQ-POWER",
|
|
250
|
+
"PrimePowerCeiling",
|
|
251
|
+
defs["PowerCeilingRequirement"],
|
|
252
|
+
"Prime power draw shall not exceed 450 W.",
|
|
253
|
+
"prime_power_w",
|
|
254
|
+
"<=",
|
|
255
|
+
450.0,
|
|
256
|
+
"W",
|
|
257
|
+
"must",
|
|
258
|
+
),
|
|
259
|
+
(
|
|
260
|
+
"REQ-COST",
|
|
261
|
+
"UnitCostCeiling",
|
|
262
|
+
defs["CostCeilingRequirement"],
|
|
263
|
+
"Recurring unit cost shall not exceed 45,000 USD from the parts table.",
|
|
264
|
+
"unit_cost_usd",
|
|
265
|
+
"<=",
|
|
266
|
+
45000.0,
|
|
267
|
+
None,
|
|
268
|
+
"must",
|
|
269
|
+
),
|
|
270
|
+
(
|
|
271
|
+
"REQ-GRATING",
|
|
272
|
+
"GratingLobeMargin",
|
|
273
|
+
defs["GratingLobeRequirement"],
|
|
274
|
+
"The lattice shall keep grating lobes out of visible space over the scan envelope.",
|
|
275
|
+
"grating_margin_lambda",
|
|
276
|
+
">=",
|
|
277
|
+
0.0,
|
|
278
|
+
None,
|
|
279
|
+
"must",
|
|
280
|
+
),
|
|
281
|
+
]
|
|
282
|
+
req_elements = {}
|
|
283
|
+
for short, name, definition, text, key, op, threshold, unit, severity in reqs:
|
|
284
|
+
req_elements[short] = _metric_requirement(
|
|
285
|
+
model,
|
|
286
|
+
pkg,
|
|
287
|
+
short,
|
|
288
|
+
name,
|
|
289
|
+
definition=definition,
|
|
290
|
+
text=text,
|
|
291
|
+
metric_key=key,
|
|
292
|
+
op=op,
|
|
293
|
+
threshold=threshold,
|
|
294
|
+
unit=unit,
|
|
295
|
+
severity=severity,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
link_budget = builder.analysis(
|
|
299
|
+
model,
|
|
300
|
+
"linkBudget",
|
|
301
|
+
owner=pkg,
|
|
302
|
+
definition=defs["LinkBudgetAnalysis"],
|
|
303
|
+
subject=terminal,
|
|
304
|
+
objective="Close the uplink worst-case over the t3-001 evaluation envelope.",
|
|
305
|
+
)
|
|
306
|
+
pattern = builder.analysis(
|
|
307
|
+
model,
|
|
308
|
+
"patternAnalysis",
|
|
309
|
+
owner=pkg,
|
|
310
|
+
definition=defs["ArrayPatternAnalysis"],
|
|
311
|
+
subject=array,
|
|
312
|
+
objective="Integrate the full pattern; recompute gain, sidelobes, grating margin.",
|
|
313
|
+
)
|
|
314
|
+
swap = builder.analysis(
|
|
315
|
+
model,
|
|
316
|
+
"swapCost",
|
|
317
|
+
owner=pkg,
|
|
318
|
+
definition=defs["SwapCostAnalysis"],
|
|
319
|
+
subject=terminal,
|
|
320
|
+
objective="Roll up prime power and unit cost from the parts table.",
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
for short, satisfier in [
|
|
324
|
+
("REQ-LINK-MARGIN", terminal),
|
|
325
|
+
("REQ-INDEP-LINK", terminal),
|
|
326
|
+
("REQ-SLL", array),
|
|
327
|
+
("REQ-GRATING", array),
|
|
328
|
+
("REQ-POWER", terminal),
|
|
329
|
+
("REQ-COST", terminal),
|
|
330
|
+
]:
|
|
331
|
+
builder.satisfy(model, source=satisfier, target=req_elements[short], owner=pkg)
|
|
332
|
+
|
|
333
|
+
for short, part in [
|
|
334
|
+
("REQ-SLL", array),
|
|
335
|
+
("REQ-GRATING", array),
|
|
336
|
+
("REQ-POWER", terminal),
|
|
337
|
+
]:
|
|
338
|
+
builder.allocate(model, source=req_elements[short], target=part, owner=pkg)
|
|
339
|
+
|
|
340
|
+
for short, analysis_usage in [
|
|
341
|
+
("REQ-LINK-MARGIN", link_budget),
|
|
342
|
+
("REQ-INDEP-LINK", link_budget),
|
|
343
|
+
("REQ-CLEARSKY-AGREE", link_budget),
|
|
344
|
+
("REQ-SLL", pattern),
|
|
345
|
+
("REQ-GAIN-XCHECK", pattern),
|
|
346
|
+
("REQ-GRATING", pattern),
|
|
347
|
+
("REQ-POWER", swap),
|
|
348
|
+
("REQ-COST", swap),
|
|
349
|
+
]:
|
|
350
|
+
builder.verify(model, source=analysis_usage, target=req_elements[short], owner=pkg)
|
|
351
|
+
|
|
352
|
+
return model
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def build_example() -> Model:
|
|
356
|
+
"""Build the library plus the SatcomTerminal28GHz example package."""
|
|
357
|
+
model = build_library()
|
|
358
|
+
return _add_example(model)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package RFAnalyses {
|
|
2
|
+
doc /* Analysis case definitions; bindings to physics engines live downstream. */
|
|
3
|
+
analysis def LinkBudgetAnalysis {
|
|
4
|
+
doc /* Worst-case link closure over an evaluation envelope. */
|
|
5
|
+
}
|
|
6
|
+
analysis def ArrayPatternAnalysis {
|
|
7
|
+
doc /* Full pattern integration: gain, sidelobes, grating lobes. */
|
|
8
|
+
}
|
|
9
|
+
analysis def ScanPerformanceAnalysis {
|
|
10
|
+
doc /* Gain and pattern degradation across the scan envelope. */
|
|
11
|
+
}
|
|
12
|
+
analysis def SwapCostAnalysis {
|
|
13
|
+
doc /* Prime power and recurring cost rollup. */
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
package RFParts {
|
|
2
|
+
doc /* Part and port definitions for phased-array antenna systems. */
|
|
3
|
+
port def RFPort {
|
|
4
|
+
doc /* Guided-wave RF interface. */
|
|
5
|
+
}
|
|
6
|
+
port def BeamPort {
|
|
7
|
+
doc /* Formed-beam signal interface. */
|
|
8
|
+
}
|
|
9
|
+
port def CtrlPort {
|
|
10
|
+
doc /* Command and telemetry interface. */
|
|
11
|
+
}
|
|
12
|
+
part def AntennaElement {
|
|
13
|
+
doc /* A single radiating element. */
|
|
14
|
+
}
|
|
15
|
+
part def RadiatingAperture {
|
|
16
|
+
doc /* The element lattice as one aperture. */
|
|
17
|
+
}
|
|
18
|
+
part def TRModule {
|
|
19
|
+
doc /* Transmit/receive module: PA, LNA, phase shifter, switch. */
|
|
20
|
+
}
|
|
21
|
+
part def Beamformer {
|
|
22
|
+
doc /* Analog, digital, or hybrid beamforming network. */
|
|
23
|
+
}
|
|
24
|
+
part def PhasedArrayAntenna {
|
|
25
|
+
doc /* Aperture, T/R modules, and beamformer as one antenna. */
|
|
26
|
+
}
|
|
27
|
+
part def RFFrontEnd {
|
|
28
|
+
doc /* Up/down conversion and filtering between antenna and modem. */
|
|
29
|
+
}
|
|
30
|
+
part def Modem {
|
|
31
|
+
doc /* Waveform modulation and demodulation. */
|
|
32
|
+
}
|
|
33
|
+
part def SatcomTerminal {
|
|
34
|
+
doc /* A complete ground terminal. */
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
package RFRequirements {
|
|
2
|
+
doc /* Requirement definitions for RF terminals. Usages own attributes metricKey (str), threshold (number with unit), op (>=, <=, ==), and optionally severity (must/should/nice); sysml2kit.interop reads them. */
|
|
3
|
+
requirement def EirpRequirement {
|
|
4
|
+
doc /* Minimum effective isotropic radiated power. */
|
|
5
|
+
}
|
|
6
|
+
requirement def GOverTRequirement {
|
|
7
|
+
doc /* Minimum receive figure of merit. */
|
|
8
|
+
}
|
|
9
|
+
requirement def SidelobeRequirement {
|
|
10
|
+
doc /* Maximum pattern sidelobe level. */
|
|
11
|
+
}
|
|
12
|
+
requirement def ScanLossRequirement {
|
|
13
|
+
doc /* Maximum gain loss at the scan-angle extreme. */
|
|
14
|
+
}
|
|
15
|
+
requirement def LinkMarginRequirement {
|
|
16
|
+
doc /* Minimum worst-case link margin. */
|
|
17
|
+
}
|
|
18
|
+
requirement def BandwidthRequirement {
|
|
19
|
+
doc /* Minimum instantaneous bandwidth. */
|
|
20
|
+
}
|
|
21
|
+
requirement def PowerCeilingRequirement {
|
|
22
|
+
doc /* Maximum prime-power draw. */
|
|
23
|
+
}
|
|
24
|
+
requirement def CostCeilingRequirement {
|
|
25
|
+
doc /* Maximum recurring unit cost. */
|
|
26
|
+
}
|
|
27
|
+
requirement def CrosscheckRequirement {
|
|
28
|
+
doc /* Maximum disagreement between independent analyses. */
|
|
29
|
+
}
|
|
30
|
+
requirement def GratingLobeRequirement {
|
|
31
|
+
doc /* Minimum grating-lobe margin in wavelengths. */
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
package RFVocabulary {
|
|
2
|
+
doc /* Quantity kinds for antenna/RF systems engineering, with canonical units. */
|
|
3
|
+
attribute def Frequency_GHz {
|
|
4
|
+
doc /* Carrier or design frequency. */
|
|
5
|
+
}
|
|
6
|
+
attribute def Bandwidth_MHz {
|
|
7
|
+
doc /* Occupied or instantaneous bandwidth. */
|
|
8
|
+
}
|
|
9
|
+
attribute def Gain_dBi {
|
|
10
|
+
doc /* Antenna gain relative to isotropic. */
|
|
11
|
+
}
|
|
12
|
+
attribute def EIRP_dBW {
|
|
13
|
+
doc /* Effective isotropic radiated power. */
|
|
14
|
+
}
|
|
15
|
+
attribute def GOverT_dBK {
|
|
16
|
+
doc /* Receive figure of merit G/T. */
|
|
17
|
+
}
|
|
18
|
+
attribute def SidelobeLevel_dB {
|
|
19
|
+
doc /* Peak sidelobe level relative to the main beam. */
|
|
20
|
+
}
|
|
21
|
+
attribute def ScanLoss_dB {
|
|
22
|
+
doc /* Gain loss at the scan angle relative to broadside. */
|
|
23
|
+
}
|
|
24
|
+
attribute def LinkMargin_dB {
|
|
25
|
+
doc /* Margin above the required SNR. */
|
|
26
|
+
}
|
|
27
|
+
attribute def NoiseFigure_dB {
|
|
28
|
+
doc /* Cascaded receive noise figure. */
|
|
29
|
+
}
|
|
30
|
+
attribute def AxialRatio_dB {
|
|
31
|
+
doc /* Polarization axial ratio. */
|
|
32
|
+
}
|
|
33
|
+
attribute def ScanAngle_deg {
|
|
34
|
+
doc /* Beam scan angle off broadside. */
|
|
35
|
+
}
|
|
36
|
+
attribute def PrimePower_W {
|
|
37
|
+
doc /* Prime (DC input) power draw. */
|
|
38
|
+
}
|
|
39
|
+
attribute def UnitCost_USD {
|
|
40
|
+
doc /* Recurring unit cost in US dollars. */
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
package SatcomTerminal28GHz {
|
|
2
|
+
public import RFVocabulary::*;
|
|
3
|
+
public import RFParts::*;
|
|
4
|
+
public import RFRequirements::*;
|
|
5
|
+
public import RFAnalyses::*;
|
|
6
|
+
doc /* 28 GHz LEO uplink phased-array ground terminal, mirroring the aedl t3-001 benchmark: close the worst-case link over the evaluation envelope inside a 450 W prime-power and 45 kUSD unit-cost ceiling. */
|
|
7
|
+
part terminal : SatcomTerminal {
|
|
8
|
+
part array : PhasedArrayAntenna {
|
|
9
|
+
attribute frequency = 28.0 [GHz];
|
|
10
|
+
attribute bandwidth = 50.0 [MHz];
|
|
11
|
+
attribute maxScanAngle = 60.0 [deg];
|
|
12
|
+
part elements : AntennaElement [1..*];
|
|
13
|
+
part trModules : TRModule [1..*];
|
|
14
|
+
part beamformer : Beamformer {
|
|
15
|
+
port beamOut : BeamPort;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
part frontEnd : RFFrontEnd {
|
|
19
|
+
port beamIn : BeamPort;
|
|
20
|
+
}
|
|
21
|
+
part modem : Modem;
|
|
22
|
+
connection beamFeed connect array.beamformer.beamOut to frontEnd.beamIn;
|
|
23
|
+
}
|
|
24
|
+
requirement <'REQ-LINK-MARGIN'> WorstCaseLinkMargin {
|
|
25
|
+
doc /* The link shall close (margin >= 0 dB) worst-case over the evaluation envelope. */
|
|
26
|
+
attribute metricKey = "worst_case_link_margin_db";
|
|
27
|
+
attribute threshold = 0.0 [dB];
|
|
28
|
+
attribute op = ">=";
|
|
29
|
+
attribute severity = "must";
|
|
30
|
+
}
|
|
31
|
+
requirement <'REQ-SLL'> PatternSidelobes {
|
|
32
|
+
doc /* Worst-case pattern sidelobe level shall not exceed -16 dB. */
|
|
33
|
+
attribute metricKey = "worst_case_pattern_sll_db";
|
|
34
|
+
attribute threshold = -16.0 [dB];
|
|
35
|
+
attribute op = "<=";
|
|
36
|
+
attribute severity = "must";
|
|
37
|
+
}
|
|
38
|
+
requirement <'REQ-INDEP-LINK'> IndependentLinkClosure {
|
|
39
|
+
doc /* The link shall also close under the independent opensatcom recomputation. */
|
|
40
|
+
attribute metricKey = "opensatcom_worst_case_margin_db";
|
|
41
|
+
attribute threshold = 0.0 [dB];
|
|
42
|
+
attribute op = ">=";
|
|
43
|
+
attribute severity = "must";
|
|
44
|
+
}
|
|
45
|
+
requirement <'REQ-CLEARSKY-AGREE'> ClearSkyAgreement {
|
|
46
|
+
doc /* Clear-sky margins from the two link analyses shall agree within 1.2 dB. */
|
|
47
|
+
attribute metricKey = "crosscheck_clearsky_margin_disagreement_db";
|
|
48
|
+
attribute threshold = 1.2 [dB];
|
|
49
|
+
attribute op = "<=";
|
|
50
|
+
attribute severity = "should";
|
|
51
|
+
}
|
|
52
|
+
requirement <'REQ-GAIN-XCHECK'> GainCrosscheck {
|
|
53
|
+
doc /* Claimed and integrated gain shall agree within 0.5 dB. */
|
|
54
|
+
attribute metricKey = "crosscheck_gain_disagreement_db";
|
|
55
|
+
attribute threshold = 0.5 [dB];
|
|
56
|
+
attribute op = "<=";
|
|
57
|
+
attribute severity = "should";
|
|
58
|
+
}
|
|
59
|
+
requirement <'REQ-POWER'> PrimePowerCeiling {
|
|
60
|
+
doc /* Prime power draw shall not exceed 450 W. */
|
|
61
|
+
attribute metricKey = "prime_power_w";
|
|
62
|
+
attribute threshold = 450.0 [W];
|
|
63
|
+
attribute op = "<=";
|
|
64
|
+
attribute severity = "must";
|
|
65
|
+
}
|
|
66
|
+
requirement <'REQ-COST'> UnitCostCeiling {
|
|
67
|
+
doc /* Recurring unit cost shall not exceed 45,000 USD from the parts table. */
|
|
68
|
+
attribute metricKey = "unit_cost_usd";
|
|
69
|
+
attribute threshold = 45000.0;
|
|
70
|
+
attribute op = "<=";
|
|
71
|
+
attribute severity = "must";
|
|
72
|
+
}
|
|
73
|
+
requirement <'REQ-GRATING'> GratingLobeMargin {
|
|
74
|
+
doc /* The lattice shall keep grating lobes out of visible space over the scan envelope. */
|
|
75
|
+
attribute metricKey = "grating_margin_lambda";
|
|
76
|
+
attribute threshold = 0.0;
|
|
77
|
+
attribute op = ">=";
|
|
78
|
+
attribute severity = "must";
|
|
79
|
+
}
|
|
80
|
+
analysis linkBudget : LinkBudgetAnalysis {
|
|
81
|
+
subject terminal;
|
|
82
|
+
objective { doc /* Close the uplink worst-case over the t3-001 evaluation envelope. */ }
|
|
83
|
+
}
|
|
84
|
+
analysis patternAnalysis : ArrayPatternAnalysis {
|
|
85
|
+
subject array;
|
|
86
|
+
objective { doc /* Integrate the full pattern; recompute gain, sidelobes, grating margin. */ }
|
|
87
|
+
}
|
|
88
|
+
analysis swapCost : SwapCostAnalysis {
|
|
89
|
+
subject terminal;
|
|
90
|
+
objective { doc /* Roll up prime power and unit cost from the parts table. */ }
|
|
91
|
+
}
|
|
92
|
+
satisfy WorstCaseLinkMargin by terminal;
|
|
93
|
+
satisfy IndependentLinkClosure by terminal;
|
|
94
|
+
satisfy PatternSidelobes by terminal.array;
|
|
95
|
+
satisfy GratingLobeMargin by terminal.array;
|
|
96
|
+
satisfy PrimePowerCeiling by terminal;
|
|
97
|
+
satisfy UnitCostCeiling by terminal;
|
|
98
|
+
allocate PatternSidelobes to terminal.array;
|
|
99
|
+
allocate GratingLobeMargin to terminal.array;
|
|
100
|
+
allocate PrimePowerCeiling to terminal;
|
|
101
|
+
dependency from linkBudget to WorstCaseLinkMargin; // verify
|
|
102
|
+
dependency from linkBudget to IndependentLinkClosure; // verify
|
|
103
|
+
dependency from linkBudget to ClearSkyAgreement; // verify
|
|
104
|
+
dependency from patternAnalysis to PatternSidelobes; // verify
|
|
105
|
+
dependency from patternAnalysis to GainCrosscheck; // verify
|
|
106
|
+
dependency from patternAnalysis to GratingLobeMargin; // verify
|
|
107
|
+
dependency from swapCost to PrimePowerCeiling; // verify
|
|
108
|
+
dependency from swapCost to UnitCostCeiling; // verify
|
|
109
|
+
}
|