[UPDATE] рефакторинг: handlers, fontspec, Docker optimization
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
texlive-xetex \
|
texlive-xetex \
|
||||||
texlive-lang-cyrillic \
|
texlive-lang-cyrillic \
|
||||||
texlive-latex-extra \
|
texlive-latex-extra \
|
||||||
texlive-fonts-extra \
|
fonts-liberation \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 614 KiB |
@@ -16,6 +16,12 @@ UNNUMBERED = {
|
|||||||
"СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ",
|
"СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NEWPAGE_BEFORE = {
|
||||||
|
"ВВЕДЕНИЕ",
|
||||||
|
"ЗАКЛЮЧЕНИЕ",
|
||||||
|
"СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def heading(r: Renderer) -> str:
|
def heading(r: Renderer) -> str:
|
||||||
tok = r.tokens[r.pos]
|
tok = r.tokens[r.pos]
|
||||||
@@ -27,10 +33,15 @@ def heading(r: Renderer) -> str:
|
|||||||
r.pos += 2
|
r.pos += 2
|
||||||
|
|
||||||
if text.upper() in UNNUMBERED:
|
if text.upper() in UNNUMBERED:
|
||||||
cmd = {1: "section*", 2: "subsection*", 3: "subsubsection*"}.get(
|
prefix = ""
|
||||||
level, "paragraph*"
|
if text.upper() in NEWPAGE_BEFORE:
|
||||||
|
prefix = "\\clearpage\n"
|
||||||
|
return (
|
||||||
|
f"{prefix}"
|
||||||
|
f"\\begin{{center}}\n"
|
||||||
|
f"\\fontsize{{14pt}}{{18pt}}\\selectfont \\bfseries {text}\n"
|
||||||
|
f"\\end{{center}}"
|
||||||
)
|
)
|
||||||
return f"\\{cmd}{{{text}}}"
|
|
||||||
|
|
||||||
if level == 1:
|
if level == 1:
|
||||||
r.section += 1
|
r.section += 1
|
||||||
@@ -47,7 +58,4 @@ def heading(r: Renderer) -> str:
|
|||||||
else:
|
else:
|
||||||
num = ""
|
num = ""
|
||||||
|
|
||||||
cmd = {1: "section", 2: "subsection", 3: "subsubsection"}.get(
|
return f"\\noindent\\textbf{{{num}{text}}}"
|
||||||
level, "paragraph"
|
|
||||||
)
|
|
||||||
return f"\\{cmd}{{{num}{text}}}"
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ def abbreviations(text: str) -> str:
|
|||||||
tabular_rows = []
|
tabular_rows = []
|
||||||
for abbr, desc in rows:
|
for abbr, desc in rows:
|
||||||
pad = max_abbr - len(abbr) + 2
|
pad = max_abbr - len(abbr) + 2
|
||||||
tabular_rows.append(f"{abbr} {' ' * pad}& {desc} \\\\")
|
tabular_rows.append(f"{abbr} {' ' * pad}& --- {desc} \\\\")
|
||||||
|
|
||||||
col_spec = f"@{{\\hspace{{0pt}}}}p{{{max_abbr + 2}em}}@{{\\hspace{{0pt}}}}l"
|
col_spec = f"@{{\\hspace{{0pt}}}}p{{{max_abbr + 2}em}}@{{\\hspace{{0pt}}}}l"
|
||||||
body = "\n".join(tabular_rows)
|
body = "\n".join(tabular_rows)
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
PREAMBLE = r"""\documentclass[a4paper, 14pt]{extarticle}
|
PREAMBLE = r"""\documentclass[a4paper, 14pt]{extarticle}
|
||||||
\usepackage{geometry}
|
\usepackage{geometry}
|
||||||
\geometry{a4paper,tmargin=2cm,bmargin=2cm,lmargin=3cm,rmargin=1cm}
|
\geometry{a4paper,tmargin=2cm,bmargin=2cm,lmargin=3cm,rmargin=1cm}
|
||||||
\usepackage[T2A]{fontenc}
|
\usepackage{fontspec}
|
||||||
\usepackage[utf8]{inputenc}
|
\setmainfont{Liberation Serif}
|
||||||
|
\setsansfont{Liberation Sans}
|
||||||
|
\setmonofont{Liberation Mono}
|
||||||
\usepackage[english, main=russian]{babel}
|
\usepackage[english, main=russian]{babel}
|
||||||
\usepackage{textcomp}
|
\usepackage{textcomp}
|
||||||
\usepackage{indentfirst}
|
\usepackage{indentfirst}
|
||||||
|
\setlength{\parindent}{1.25cm}
|
||||||
|
\usepackage{setspace}
|
||||||
|
\onehalfspacing
|
||||||
\usepackage{graphicx}
|
\usepackage{graphicx}
|
||||||
\graphicspath{ {./images/} }
|
\graphicspath{ {./} {./images/} }
|
||||||
\usepackage{wrapfig}
|
\usepackage{wrapfig}
|
||||||
\linespread{1.3}
|
|
||||||
\sloppy
|
\sloppy
|
||||||
\clubpenalty=10000
|
\clubpenalty=10000
|
||||||
\widowpenalty=10000
|
\widowpenalty=10000
|
||||||
\usepackage{enumitem}
|
\usepackage{enumitem}
|
||||||
|
\setlist[enumerate,1]{label=\arabic*., leftmargin=1.25cm, itemsep=0pt, parsep=0pt}
|
||||||
\usepackage{caption}
|
\usepackage{caption}
|
||||||
\usepackage{hyperref}
|
\usepackage{hyperref}
|
||||||
\hypersetup {unicode=true}
|
\hypersetup {unicode=true}
|
||||||
\DeclareCaptionLabelSeparator*{emdash}{~--- }
|
\DeclareCaptionLabelSeparator*{emdash}{~--- }
|
||||||
\captionsetup[figure]{labelsep=emdash,font=onehalfspacing,position=bottom}
|
\captionsetup[figure]{labelsep=emdash,font=onehalfspacing,position=bottom}
|
||||||
\usepackage{mathptmx}
|
|
||||||
\usepackage{listings}
|
\usepackage{listings}
|
||||||
\lstset{
|
\lstset{
|
||||||
basicstyle=\ttfamily\footnotesize,
|
basicstyle=\ttfamily\footnotesize,
|
||||||
|
|||||||
@@ -8,12 +8,13 @@ def render_titlepage(fm: dict[str, str]) -> str:
|
|||||||
student = fm.get("студент", "")
|
student = fm.get("студент", "")
|
||||||
advisor = fm.get("преподаватель", "")
|
advisor = fm.get("преподаватель", "")
|
||||||
group = fm.get("группа", "")
|
group = fm.get("группа", "")
|
||||||
|
year = fm.get("год", "2026")
|
||||||
|
|
||||||
header_lines = [
|
header_lines = [
|
||||||
r"\vspace*{-0.7ex}",
|
r"\vspace*{-0.7ex}",
|
||||||
r"\hspace*{-6pt}\centerline{Министерство науки и высшего образования Российской Федерации}",
|
r"\hspace*{-6pt}\centerline{Министерство науки и высшего образования Российской Федерации}",
|
||||||
r"\vspace*{-0.7ex}",
|
r"\vspace*{-0.7ex}",
|
||||||
r"\centerline{Федеральное государственное бюджетное образовательное учреждение }",
|
r"\centerline{Федеральное государственное автономное образовательное учреждение}",
|
||||||
r"\vspace*{-0.7ex}",
|
r"\vspace*{-0.7ex}",
|
||||||
r"\centerline{высшего образования}",
|
r"\centerline{высшего образования}",
|
||||||
r"\vspace*{-0.7ex}",
|
r"\vspace*{-0.7ex}",
|
||||||
@@ -21,6 +22,8 @@ def render_titlepage(fm: dict[str, str]) -> str:
|
|||||||
r"\vspace*{-0.7ex}",
|
r"\vspace*{-0.7ex}",
|
||||||
r"\centerline{имени Н.Э. Баумана>>}",
|
r"\centerline{имени Н.Э. Баумана>>}",
|
||||||
r"\vspace*{-0.7ex}",
|
r"\vspace*{-0.7ex}",
|
||||||
|
r"\centerline{(национальный исследовательский университет)>>}",
|
||||||
|
r"\vspace*{-0.7ex}",
|
||||||
r"\centerline{(МГТУ им. Н.Э. Баумана)}",
|
r"\centerline{(МГТУ им. Н.Э. Баумана)}",
|
||||||
]
|
]
|
||||||
header = "\n".join(header_lines)
|
header = "\n".join(header_lines)
|
||||||
@@ -62,23 +65,24 @@ def render_titlepage(fm: dict[str, str]) -> str:
|
|||||||
"\\vspace{11em}\n"
|
"\\vspace{11em}\n"
|
||||||
"\n"
|
"\n"
|
||||||
"\\begin{center}\n"
|
"\\begin{center}\n"
|
||||||
f"\\Large {{\\bf {title}}} \\\\\n"
|
f"\\fontsize{{22pt}}{{26pt}}\\selectfont \\bfseries {title} \\\\\n"
|
||||||
"\\end{center}\\normalsize\n"
|
"\\end{center}\n"
|
||||||
"\n"
|
"\n"
|
||||||
"\\vspace{8em}\n"
|
"\\vspace{8em}\n"
|
||||||
"\n"
|
"\n"
|
||||||
"\\begin{flushright}\n"
|
"\\noindent Студент группы " + group + "\n"
|
||||||
f" {{Студент группы {group} {student} \\hspace*{{15pt}}\\\\\n"
|
"\\hfill \\underline{\\hspace{3cm}} \\hfill \\underline{\\hspace{4cm}} \\\\\n"
|
||||||
|
"\\hfill (Группа) \\hfill (Подпись, дата) \\hfill (И.О. Фамилия) \\\\\n"
|
||||||
"\\vspace{2ex}\n"
|
"\\vspace{2ex}\n"
|
||||||
f" Преподаватель {advisor}\\hspace*{{15pt}}}}\n"
|
|
||||||
"\\end{flushright}\n"
|
|
||||||
"\n"
|
"\n"
|
||||||
"\\bigskip\n"
|
"\\noindent Преподаватель " + advisor + "\n"
|
||||||
|
"\\hfill \\underline{\\hspace{3cm}} \\hfill \\underline{\\hspace{4cm}} \\\\\n"
|
||||||
|
"\\hfill \\hfill (Подпись, дата) \\hfill (И.О. Фамилия)\n"
|
||||||
"\n"
|
"\n"
|
||||||
"\\vfill\n"
|
"\\vfill\n"
|
||||||
"\n"
|
"\n"
|
||||||
"\\begin{center}\n"
|
"\\begin{center}\n"
|
||||||
"\\textsl{Москва 2025}\n"
|
f"\\textsl{{{year} г.}}\n"
|
||||||
"\\end{center}\n"
|
"\\end{center}\n"
|
||||||
"\\end{titlepage}"
|
"\\end{titlepage}"
|
||||||
)
|
)
|
||||||
|
|||||||
+23
-8
@@ -7,6 +7,16 @@ from md2gost.parser import Parser
|
|||||||
from md2gost.render import Renderer
|
from md2gost.render import Renderer
|
||||||
|
|
||||||
|
|
||||||
|
def run_xelatex(tex_path: Path, output_dir: Path) -> bool:
|
||||||
|
result = subprocess.run(
|
||||||
|
["xelatex", "-interaction=nonstopmode",
|
||||||
|
"-output-directory", str(output_dir), str(tex_path)],
|
||||||
|
capture_output=True, text=True,
|
||||||
|
)
|
||||||
|
pdf_path = output_dir / tex_path.with_suffix(".pdf").name
|
||||||
|
return pdf_path.exists()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
cli = argparse.ArgumentParser(
|
cli = argparse.ArgumentParser(
|
||||||
prog="md2gost",
|
prog="md2gost",
|
||||||
@@ -40,16 +50,21 @@ def main():
|
|||||||
|
|
||||||
if not args.no_pdf:
|
if not args.no_pdf:
|
||||||
pdf_path = args.output / args.input.with_suffix(".pdf").name
|
pdf_path = args.output / args.input.with_suffix(".pdf").name
|
||||||
result = subprocess.run(
|
log_path = args.output / "report.log"
|
||||||
["xelatex", "-interaction=nonstopmode", "-output-directory", str(args.output), str(tex_path)],
|
|
||||||
capture_output=True, text=True,
|
if not run_xelatex(tex_path, args.output):
|
||||||
)
|
print("ошибка: xelatex не собрал PDF", file=sys.stderr)
|
||||||
if not pdf_path.exists():
|
if log_path.exists():
|
||||||
print(result.stdout[-2000:], file=sys.stderr)
|
for line in log_path.read_text(errors="replace").splitlines():
|
||||||
|
if line.startswith("!"):
|
||||||
|
print(f" {line}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
if not run_xelatex(tex_path, args.output):
|
||||||
|
print("ошибка: xelatex (2-й проход) не собрал PDF", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
print(f"→ {pdf_path}")
|
print(f"→ {pdf_path}")
|
||||||
if result.returncode != 0:
|
|
||||||
print(" (предупреждения xelatex, PDF собран)", file=sys.stderr)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
+36
-19
@@ -16,6 +16,34 @@ from md2gost.latex import END_DOCUMENT, PREAMBLE, render_titlepage
|
|||||||
from markdown_it.token import Token
|
from markdown_it.token import Token
|
||||||
|
|
||||||
|
|
||||||
|
def _fence(r, tok):
|
||||||
|
return fence(r, tok)
|
||||||
|
|
||||||
|
|
||||||
|
def _image(r, tok):
|
||||||
|
return image(r, tok)
|
||||||
|
|
||||||
|
|
||||||
|
def _inline(r, tok):
|
||||||
|
return render_inline(tok)
|
||||||
|
|
||||||
|
|
||||||
|
_BLOCK = {
|
||||||
|
"heading_open": heading,
|
||||||
|
"paragraph_open": paragraph,
|
||||||
|
"bullet_list_open": bullet_list,
|
||||||
|
"ordered_list_open": ordered_list,
|
||||||
|
"table_open": table,
|
||||||
|
"hr": hr,
|
||||||
|
}
|
||||||
|
|
||||||
|
_INLINE = {
|
||||||
|
"fence": _fence,
|
||||||
|
"image": _image,
|
||||||
|
"inline": _inline,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Renderer:
|
class Renderer:
|
||||||
def __init__(self, tokens: list[Token], front_matter: dict[str, str]) -> None:
|
def __init__(self, tokens: list[Token], front_matter: dict[str, str]) -> None:
|
||||||
self.tokens = tokens
|
self.tokens = tokens
|
||||||
@@ -29,29 +57,18 @@ class Renderer:
|
|||||||
def render(self) -> str:
|
def render(self) -> str:
|
||||||
parts: list[str] = [PREAMBLE, render_titlepage(self.fm)]
|
parts: list[str] = [PREAMBLE, render_titlepage(self.fm)]
|
||||||
|
|
||||||
dispatch = {
|
|
||||||
"heading_open": lambda: heading(self),
|
|
||||||
"paragraph_open": lambda: paragraph(self),
|
|
||||||
"bullet_list_open": lambda: bullet_list(self),
|
|
||||||
"ordered_list_open": lambda: ordered_list(self),
|
|
||||||
"table_open": lambda: table(self),
|
|
||||||
"hr": lambda: hr(self),
|
|
||||||
"front_matter": None,
|
|
||||||
}
|
|
||||||
|
|
||||||
while self.pos < len(self.tokens):
|
while self.pos < len(self.tokens):
|
||||||
tok = self.tokens[self.pos]
|
tok = self.tokens[self.pos]
|
||||||
prev_pos = self.pos
|
prev_pos = self.pos
|
||||||
|
tt = tok.type
|
||||||
|
|
||||||
handler = dispatch.get(tok.type)
|
if tt in _BLOCK:
|
||||||
if handler is not None:
|
result = _BLOCK[tt](self)
|
||||||
parts.append(handler())
|
parts.append(result)
|
||||||
elif tok.type == "fence":
|
if self.current_heading == "СОДЕРЖАНИЕ":
|
||||||
parts.append(fence(self, tok))
|
parts.append("\\newpage\n\\tableofcontents\n\\newpage")
|
||||||
elif tok.type == "image":
|
elif tt in _INLINE:
|
||||||
parts.append(image(self, tok))
|
parts.append(_INLINE[tt](self, tok))
|
||||||
elif tok.type == "inline":
|
|
||||||
parts.append(render_inline(tok))
|
|
||||||
else:
|
else:
|
||||||
self.pos += 1
|
self.pos += 1
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
---
|
||||||
|
факультет: «Информатика и системы управления»
|
||||||
|
кафедра: «Теоретическая информатика и компьютерные технологии»
|
||||||
|
название: Отчёт о летней практике
|
||||||
|
студент: А. С. Булдаков
|
||||||
|
преподаватель: Д. П. Посевин
|
||||||
|
группа: ИУ9-22Б
|
||||||
|
---
|
||||||
|
|
||||||
|
# АННОТАЦИЯ
|
||||||
|
|
||||||
|
В настоящем отчёте описывается разработка серверной части программно-аппаратного комплекса мониторинга радиоэфира, предназначенного для обнаружения источников радиосигнала и вычисления их координат методом триангуляции.
|
||||||
|
|
||||||
|
Актуальность разработки обусловлена возрастающей потребностью в контроле радиоэлектронной обстановки для обнаружения неавторизованных передающих устройств.
|
||||||
|
|
||||||
|
Разработано серверное приложение на языке C++, включающее TCP-сервер для приёма данных от трёх программно-определяемых радиосистем, модуль вычисления координат источника на основе модели логарифмического затухания сигнала и метода роевой оптимизации, а также графический интерфейс на базе фреймворка Qt. Обмен данными между модулями реализован через потокобезопасную очередь с использованием условной переменной.
|
||||||
|
|
||||||
|
# СОДЕРЖАНИЕ
|
||||||
|
|
||||||
|
# ПЕРЕЧЕНЬ ПРИНЯТЫХ СОКРАЩЕНИЙ
|
||||||
|
|
||||||
|
SDR — Software-Defined Radio — программно-определяемая радиосистема
|
||||||
|
PSO — Particle Swarm Optimization — метод роевой оптимизации
|
||||||
|
TCP — Transmission Control Protocol — протокол управления передачей
|
||||||
|
GUI — Graphical User Interface — графический интерфейс пользователя
|
||||||
|
|
||||||
|
# ВВЕДЕНИЕ
|
||||||
|
|
||||||
|
Количество радиоэлектронных устройств в повседневной жизни неуклонно растёт. Помимо легитимных передатчиков в радиоэфире появляются устройства, эксплуатация которых несанкционирована или представляет потенциальную угрозу [1]. Для принятия решения о дальнейших действиях относительно обнаруженного источника радиосигнала необходимо не только зафиксировать сам факт его наличия, но и определить координаты передающего устройства [2].
|
||||||
|
|
||||||
|
Существующие решения для мониторинга радиоэфира, как правило, представляют собой дорогостоящие комплексные системы, требующие специализированного оборудования и инфраструктуры. Альтернативным подходом является использование компактных программно-определяемых радиосистем, способных передавать данные на обычные вычислительные модули для последующей обработки.
|
||||||
|
|
||||||
|
Целью работы является разработка серверной части программно-аппаратного комплекса мониторинга радиоэфира, обеспечивающего приём данных от программно-определяемых радиосистем и вычисление координат источников радиосигнала.
|
||||||
|
|
||||||
|
Для достижения поставленной цели необходимо решить следующие задачи:
|
||||||
|
|
||||||
|
1. Разработать TCP-сервер для приёма и обработки данных от трёх SDR-приёмников
|
||||||
|
2. Реализовать алгоритм триангуляции на основе модели логарифмического затухания сигнала и метода роевой оптимизации [3]
|
||||||
|
3. Разработать графический интерфейс для визуализации результатов пеленгации
|
||||||
|
4. Обеспечить взаимодействие модулей через потокобезопасную очередь
|
||||||
|
5. Настроить контейнеризацию приложения с помощью Docker
|
||||||
|
|
||||||
|
# Общее описание системы
|
||||||
|
|
||||||
|
# Выполненные работы
|
||||||
|
|
||||||
|
# ЗАКЛЮЧЕНИЕ
|
||||||
|
|
||||||
|
# СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
|
||||||
|
|
||||||
|
1. Гилев, И. В. Использование технологии SDR для анализа электромагнитной обстановки в системах связи специального назначения / И. В. Гилев, А. Р. Бушуева // Актуальные проблемы деятельности подразделений уголовно-исполнительной системы : сборник материалов Всероссийской научно-практической конференции. В 3 т., Воронеж, 23 октября 2025 года. – Воронеж: Федеральное казенное образовательное учреждение высшего профессионального образования "Воронежский институт Федеральной службы исполнения наказаний России", Издательско-полиграфический центр «Научная книга», 2025. – С. 324-328.
|
||||||
|
|
||||||
|
2. Шалаев, А. А. Оценка фактических значений отношения сигнал/шум в радионавигационных системах на основе экспериментальных данных / А. А. Шалаев // Crede Experto: транспорт, общество, образование, язык. – 2025. – № 4. – С. 61-76.
|
||||||
|
|
||||||
|
3. Семенюк, С. С. Определение местоположения источников радиоизлучений разностно-дальномерным методом на основе роевого интеллекта / С. С. Семенюк, А. В. Каблуков, А. С. Григоров // Динамика сложных систем - XXI век. – 2024. – Т. 18, № 1. – С. 51-57.
|
||||||
Reference in New Issue
Block a user