[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-lang-cyrillic \
|
||||
texlive-latex-extra \
|
||||
texlive-fonts-extra \
|
||||
fonts-liberation \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
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:
|
||||
tok = r.tokens[r.pos]
|
||||
@@ -27,10 +33,15 @@ def heading(r: Renderer) -> str:
|
||||
r.pos += 2
|
||||
|
||||
if text.upper() in UNNUMBERED:
|
||||
cmd = {1: "section*", 2: "subsection*", 3: "subsubsection*"}.get(
|
||||
level, "paragraph*"
|
||||
prefix = ""
|
||||
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:
|
||||
r.section += 1
|
||||
@@ -47,7 +58,4 @@ def heading(r: Renderer) -> str:
|
||||
else:
|
||||
num = ""
|
||||
|
||||
cmd = {1: "section", 2: "subsection", 3: "subsubsection"}.get(
|
||||
level, "paragraph"
|
||||
)
|
||||
return f"\\{cmd}{{{num}{text}}}"
|
||||
return f"\\noindent\\textbf{{{num}{text}}}"
|
||||
|
||||
@@ -38,7 +38,7 @@ def abbreviations(text: str) -> str:
|
||||
tabular_rows = []
|
||||
for abbr, desc in rows:
|
||||
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"
|
||||
body = "\n".join(tabular_rows)
|
||||
|
||||
@@ -1,25 +1,29 @@
|
||||
PREAMBLE = r"""\documentclass[a4paper, 14pt]{extarticle}
|
||||
\usepackage{geometry}
|
||||
\geometry{a4paper,tmargin=2cm,bmargin=2cm,lmargin=3cm,rmargin=1cm}
|
||||
\usepackage[T2A]{fontenc}
|
||||
\usepackage[utf8]{inputenc}
|
||||
\usepackage{fontspec}
|
||||
\setmainfont{Liberation Serif}
|
||||
\setsansfont{Liberation Sans}
|
||||
\setmonofont{Liberation Mono}
|
||||
\usepackage[english, main=russian]{babel}
|
||||
\usepackage{textcomp}
|
||||
\usepackage{indentfirst}
|
||||
\setlength{\parindent}{1.25cm}
|
||||
\usepackage{setspace}
|
||||
\onehalfspacing
|
||||
\usepackage{graphicx}
|
||||
\graphicspath{ {./images/} }
|
||||
\graphicspath{ {./} {./images/} }
|
||||
\usepackage{wrapfig}
|
||||
\linespread{1.3}
|
||||
\sloppy
|
||||
\clubpenalty=10000
|
||||
\widowpenalty=10000
|
||||
\usepackage{enumitem}
|
||||
\setlist[enumerate,1]{label=\arabic*., leftmargin=1.25cm, itemsep=0pt, parsep=0pt}
|
||||
\usepackage{caption}
|
||||
\usepackage{hyperref}
|
||||
\hypersetup {unicode=true}
|
||||
\DeclareCaptionLabelSeparator*{emdash}{~--- }
|
||||
\captionsetup[figure]{labelsep=emdash,font=onehalfspacing,position=bottom}
|
||||
\usepackage{mathptmx}
|
||||
\usepackage{listings}
|
||||
\lstset{
|
||||
basicstyle=\ttfamily\footnotesize,
|
||||
|
||||
@@ -8,12 +8,13 @@ def render_titlepage(fm: dict[str, str]) -> str:
|
||||
student = fm.get("студент", "")
|
||||
advisor = fm.get("преподаватель", "")
|
||||
group = fm.get("группа", "")
|
||||
year = fm.get("год", "2026")
|
||||
|
||||
header_lines = [
|
||||
r"\vspace*{-0.7ex}",
|
||||
r"\hspace*{-6pt}\centerline{Министерство науки и высшего образования Российской Федерации}",
|
||||
r"\vspace*{-0.7ex}",
|
||||
r"\centerline{Федеральное государственное бюджетное образовательное учреждение }",
|
||||
r"\centerline{Федеральное государственное автономное образовательное учреждение}",
|
||||
r"\vspace*{-0.7ex}",
|
||||
r"\centerline{высшего образования}",
|
||||
r"\vspace*{-0.7ex}",
|
||||
@@ -21,6 +22,8 @@ def render_titlepage(fm: dict[str, str]) -> str:
|
||||
r"\vspace*{-0.7ex}",
|
||||
r"\centerline{имени Н.Э. Баумана>>}",
|
||||
r"\vspace*{-0.7ex}",
|
||||
r"\centerline{(национальный исследовательский университет)>>}",
|
||||
r"\vspace*{-0.7ex}",
|
||||
r"\centerline{(МГТУ им. Н.Э. Баумана)}",
|
||||
]
|
||||
header = "\n".join(header_lines)
|
||||
@@ -62,23 +65,24 @@ def render_titlepage(fm: dict[str, str]) -> str:
|
||||
"\\vspace{11em}\n"
|
||||
"\n"
|
||||
"\\begin{center}\n"
|
||||
f"\\Large {{\\bf {title}}} \\\\\n"
|
||||
"\\end{center}\\normalsize\n"
|
||||
f"\\fontsize{{22pt}}{{26pt}}\\selectfont \\bfseries {title} \\\\\n"
|
||||
"\\end{center}\n"
|
||||
"\n"
|
||||
"\\vspace{8em}\n"
|
||||
"\n"
|
||||
"\\begin{flushright}\n"
|
||||
f" {{Студент группы {group} {student} \\hspace*{{15pt}}\\\\\n"
|
||||
"\\noindent Студент группы " + group + "\n"
|
||||
"\\hfill \\underline{\\hspace{3cm}} \\hfill \\underline{\\hspace{4cm}} \\\\\n"
|
||||
"\\hfill (Группа) \\hfill (Подпись, дата) \\hfill (И.О. Фамилия) \\\\\n"
|
||||
"\\vspace{2ex}\n"
|
||||
f" Преподаватель {advisor}\\hspace*{{15pt}}}}\n"
|
||||
"\\end{flushright}\n"
|
||||
"\n"
|
||||
"\\bigskip\n"
|
||||
"\\noindent Преподаватель " + advisor + "\n"
|
||||
"\\hfill \\underline{\\hspace{3cm}} \\hfill \\underline{\\hspace{4cm}} \\\\\n"
|
||||
"\\hfill \\hfill (Подпись, дата) \\hfill (И.О. Фамилия)\n"
|
||||
"\n"
|
||||
"\\vfill\n"
|
||||
"\n"
|
||||
"\\begin{center}\n"
|
||||
"\\textsl{Москва 2025}\n"
|
||||
f"\\textsl{{{year} г.}}\n"
|
||||
"\\end{center}\n"
|
||||
"\\end{titlepage}"
|
||||
)
|
||||
|
||||
+23
-8
@@ -7,6 +7,16 @@ from md2gost.parser import Parser
|
||||
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():
|
||||
cli = argparse.ArgumentParser(
|
||||
prog="md2gost",
|
||||
@@ -40,16 +50,21 @@ def main():
|
||||
|
||||
if not args.no_pdf:
|
||||
pdf_path = args.output / args.input.with_suffix(".pdf").name
|
||||
result = subprocess.run(
|
||||
["xelatex", "-interaction=nonstopmode", "-output-directory", str(args.output), str(tex_path)],
|
||||
capture_output=True, text=True,
|
||||
)
|
||||
if not pdf_path.exists():
|
||||
print(result.stdout[-2000:], file=sys.stderr)
|
||||
log_path = args.output / "report.log"
|
||||
|
||||
if not run_xelatex(tex_path, args.output):
|
||||
print("ошибка: xelatex не собрал PDF", file=sys.stderr)
|
||||
if log_path.exists():
|
||||
for line in log_path.read_text(errors="replace").splitlines():
|
||||
if line.startswith("!"):
|
||||
print(f" {line}", file=sys.stderr)
|
||||
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}")
|
||||
if result.returncode != 0:
|
||||
print(" (предупреждения xelatex, PDF собран)", file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
+36
-19
@@ -16,6 +16,34 @@ from md2gost.latex import END_DOCUMENT, PREAMBLE, render_titlepage
|
||||
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:
|
||||
def __init__(self, tokens: list[Token], front_matter: dict[str, str]) -> None:
|
||||
self.tokens = tokens
|
||||
@@ -29,29 +57,18 @@ class Renderer:
|
||||
def render(self) -> str:
|
||||
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):
|
||||
tok = self.tokens[self.pos]
|
||||
prev_pos = self.pos
|
||||
tt = tok.type
|
||||
|
||||
handler = dispatch.get(tok.type)
|
||||
if handler is not None:
|
||||
parts.append(handler())
|
||||
elif tok.type == "fence":
|
||||
parts.append(fence(self, tok))
|
||||
elif tok.type == "image":
|
||||
parts.append(image(self, tok))
|
||||
elif tok.type == "inline":
|
||||
parts.append(render_inline(tok))
|
||||
if tt in _BLOCK:
|
||||
result = _BLOCK[tt](self)
|
||||
parts.append(result)
|
||||
if self.current_heading == "СОДЕРЖАНИЕ":
|
||||
parts.append("\\newpage\n\\tableofcontents\n\\newpage")
|
||||
elif tt in _INLINE:
|
||||
parts.append(_INLINE[tt](self, tok))
|
||||
else:
|
||||
self.pos += 1
|
||||
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