Skip to content

Commit ba74cc5

Browse files
authored
Merge pull request #7 from MrClock8163/feature/inclination
Inclination measurement
2 parents 9e92f89 + 3f67057 commit ba74cc5

File tree

12 files changed

+339
-80
lines changed

12 files changed

+339
-80
lines changed

src/instrumentman/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from . import setup
1111
from . import setmeasurement
1212
from . import geocomtest
13+
from . import inclination
1314

1415

1516
@extra_group("iman", params=None) # type: ignore[misc]
@@ -53,8 +54,11 @@ def cli_test() -> None:
5354
cli.add_command(terminal.cli)
5455
cli_measure.add_command(setmeasurement.cli_measure)
5556
cli_measure.add_command(setup.cli_measure)
57+
cli_measure.add_command(inclination.cli_measure)
5658
cli_calc.add_command(setmeasurement.cli_calc)
59+
cli_calc.add_command(inclination.cli_calc)
5760
cli_test.add_command(geocomtest.cli)
5861
cli_merge.add_command(setmeasurement.cli_merge)
62+
cli_merge.add_command(inclination.cli_merge)
5963
cli_validate.add_command(setmeasurement.cli_validate)
6064
cli_import.add_command(setup.cli_import)

src/instrumentman/calculations.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import math
2+
3+
4+
def adjust_uniform_single(values: list[float]) -> tuple[float, float]:
5+
n = len(values)
6+
adjusted = math.fsum(values) / n
7+
dev = math.sqrt(math.fsum([(v - adjusted)**2 for v in values]) / n)
8+
return adjusted, dev

src/instrumentman/geocomtest/__init__.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
from click_extra import extra_command
44

55
from ..utils import (
6-
com_baud_option,
7-
com_timeout_option,
6+
com_option_group,
87
com_port_argument
98
)
109

@@ -15,8 +14,7 @@
1514
context_settings={"auto_envvar_prefix": None}
1615
) # type: ignore[misc]
1716
@com_port_argument()
18-
@com_baud_option()
19-
@com_timeout_option()
17+
@com_option_group()
2018
def cli(**kwargs: Any) -> None:
2119
"""Test the availability of various GeoCom protocol functions on an
2220
instrument."""

src/instrumentman/geocomtest/app.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,17 @@ def tests(tps: GeoCom) -> None:
5858
def main(
5959
port: str,
6060
baud: int = 9600,
61-
timeout: int = 15
61+
timeout: int = 15,
62+
retry: int = 1,
63+
sync_after_timeout: bool = False
6264
) -> None:
6365
try:
6466
with open_serial(
6567
port,
6668
speed=baud,
67-
timeout=timeout
69+
timeout=timeout,
70+
retry=retry,
71+
sync_after_timeout=sync_after_timeout
6872
) as com:
6973
tps = GeoCom(com)
7074
tests(tps)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
from typing import Any
2+
3+
from click_extra import (
4+
extra_command,
5+
argument,
6+
option,
7+
IntRange,
8+
File
9+
)
10+
11+
from ..utils import (
12+
com_option_group,
13+
com_port_argument
14+
)
15+
16+
17+
@extra_command(
18+
"inclination",
19+
params=None,
20+
context_settings={"auto_envvar_prefix": None}
21+
) # type: ignore[misc]
22+
@com_port_argument()
23+
@com_option_group()
24+
@option(
25+
"-o",
26+
"--output",
27+
help="file to save output to",
28+
type=File("wt", encoding="utf8", lazy=True)
29+
)
30+
@option(
31+
"-p",
32+
"--positions",
33+
help="number of positions to measure around the circle",
34+
type=IntRange(1, 12),
35+
default=1
36+
)
37+
@option(
38+
"-z",
39+
"--zero",
40+
help="start from hz==0 (otherwise start from current orientation)",
41+
is_flag=True
42+
)
43+
@option(
44+
"-c",
45+
"--cycles",
46+
help="repetition cycles",
47+
type=IntRange(1),
48+
default=1
49+
)
50+
def cli_measure(**kwargs: Any) -> None:
51+
"""Measure instrument inclination in multiple positions."""
52+
from .app import main_measure
53+
54+
main_measure(**kwargs)
55+
56+
57+
@extra_command(
58+
"inclination",
59+
params=None,
60+
context_settings={"auto_envvar_prefix": None}
61+
) # type: ignore[misc]
62+
@argument(
63+
"input",
64+
help="inclination measurement file to process",
65+
type=File("rt", encoding="utf8")
66+
)
67+
@option(
68+
"-o",
69+
"--output",
70+
help="file to save results to in CSV format",
71+
type=File("wt", encoding="utf8", lazy=True)
72+
)
73+
def cli_calc(**kwargs: Any) -> None:
74+
"""Calculate inclination from multiple measurements."""
75+
from .app import main_calc
76+
77+
main_calc(**kwargs)
78+
79+
80+
@extra_command(
81+
"inclination",
82+
params=None,
83+
context_settings={"auto_envvar_prefix": None}
84+
) # type: ignore[misc]
85+
@argument(
86+
"output",
87+
help="output file",
88+
type=File("wt", encoding="utf8", lazy=True)
89+
)
90+
@argument(
91+
"inputs",
92+
help="inclination measurement files",
93+
type=File("rt", encoding="utf8"),
94+
nargs=-1,
95+
required=True
96+
)
97+
def cli_merge(**kwargs: Any) -> None:
98+
"""Merge results from multiple inclination measurements."""
99+
from .app import main_merge
100+
101+
main_merge(**kwargs)
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
from io import TextIOWrapper
2+
from time import sleep
3+
from math import tan, atan, degrees
4+
from re import compile
5+
6+
from click_extra import echo
7+
from geocompy.data import Angle, Coordinate
8+
from geocompy.geo import GeoCom
9+
from geocompy.communication import open_serial
10+
11+
from ..calculations import adjust_uniform_single
12+
from ..utils import echo_green
13+
14+
15+
_LINE = compile(r"^\d+(?:\.\d+)?(?:,\-?\d+\.\d+){2}$")
16+
17+
18+
def run_measure(
19+
tps: GeoCom,
20+
output: TextIOWrapper | None = None,
21+
positions: int = 1,
22+
zero: bool = False,
23+
cycles: int = 1
24+
) -> None:
25+
turn = 360 // positions
26+
v = Angle(90, 'deg')
27+
start = 0
28+
29+
if not zero:
30+
angles = tps.tmc.get_angle()
31+
if angles.params is not None:
32+
start = round(angles.params[0].asunit('deg'))
33+
34+
echo(
35+
"hz_deg,cross_sec,length_sec",
36+
output
37+
)
38+
39+
for a in range(start, start + cycles * 360, turn):
40+
hz = Angle(a, 'deg').normalized()
41+
tps.aut.turn_to(hz, v)
42+
43+
sleep(1)
44+
fullangles = tps.tmc.get_angle_inclination('MEASURE')
45+
if fullangles.params is None:
46+
continue
47+
48+
az = fullangles.params[0]
49+
cross = fullangles.params[4]
50+
length = fullangles.params[5]
51+
52+
echo(
53+
f"{az.asunit('deg'):.4f},"
54+
f"{cross.asunit('deg') * 3600:.2f},"
55+
f"{length.asunit('deg') * 3600:.2f}",
56+
output
57+
)
58+
59+
60+
def main_measure(
61+
port: str,
62+
baud: int = 9600,
63+
timeout: int = 15,
64+
retry: int = 1,
65+
sync_after_timeout: bool = False,
66+
output: TextIOWrapper | None = None,
67+
positions: int = 1,
68+
zero: bool = False,
69+
cycles: int = 1
70+
) -> None:
71+
with open_serial(
72+
port,
73+
retry=retry,
74+
sync_after_timeout=sync_after_timeout,
75+
speed=baud,
76+
timeout=timeout
77+
) as com:
78+
tps = GeoCom(com)
79+
run_measure(tps, output, positions, zero, cycles)
80+
81+
82+
def main_merge(
83+
inputs: list[TextIOWrapper],
84+
output: TextIOWrapper
85+
) -> None:
86+
echo("hz_deg,cross_sec,length_sec", output)
87+
for item in inputs:
88+
for line in item:
89+
if not _LINE.match(line.strip()):
90+
continue
91+
92+
echo(line, output, False)
93+
94+
echo_green(f"Merged measurements from {len(inputs)} files.")
95+
96+
97+
def main_calc(
98+
input: TextIOWrapper,
99+
output: TextIOWrapper | None = None
100+
) -> None:
101+
points: list[Coordinate] = []
102+
103+
for line in input:
104+
if not _LINE.match(line.strip()):
105+
continue
106+
107+
fields = line.strip().split(",")
108+
azimut = Angle(float(fields[0]), 'deg')
109+
cross = Angle(float(fields[1]) / 3600, 'deg')
110+
length = Angle(float(fields[2]) / 3600, 'deg')
111+
112+
coord = Coordinate(tan(cross), tan(length), 1)
113+
bearing, inclination, s = coord.to_polar()
114+
115+
points.append(
116+
Coordinate.from_polar(
117+
(bearing + azimut).normalized(),
118+
inclination,
119+
s
120+
)
121+
)
122+
123+
x, x_dev = adjust_uniform_single([p.x for p in points])
124+
y, y_dev = adjust_uniform_single([p.y for p in points])
125+
126+
inc_x = degrees(atan(x)) * 3600
127+
inc_y = degrees(atan(y)) * 3600
128+
inc_x_dev = degrees(atan(x_dev)) * 3600
129+
inc_y_dev = degrees(atan(y_dev)) * 3600
130+
131+
direction, inc, _ = Coordinate(x, y, 1).to_polar()
132+
133+
if output is None:
134+
echo(f"""Axis aligned:
135+
inclination X: {inc_x:.1f}" +/- {inc_x_dev:.1f}"
136+
inclination Y: {inc_y:.1f}" +/- {inc_y_dev:.1f}"
137+
Polar:
138+
direction: {direction.asunit('deg'):.4f}°
139+
inclination: {inc.asunit('deg') * 3600:.1f}\""""
140+
)
141+
return
142+
143+
echo(
144+
"inc_x_sec,inc_x_dev_sec,inc_y_sec,inc_y_dev_sec,dir_deg,inc_sec",
145+
output
146+
)
147+
148+
echo(
149+
(
150+
f"{inc_x:.1f},{inc_x_dev:.1f},{inc_y:.1f},{inc_y_dev:.1f},"
151+
f"{direction.asunit('deg'):.4f},{inc.asunit('deg') * 3600:.1f}"
152+
),
153+
output
154+
)

src/instrumentman/morse/__init__.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@
33
from click_extra import (
44
extra_command,
55
option,
6-
option_group,
76
argument,
87
Choice,
98
IntRange
109
)
1110

1211
from ..utils import (
13-
com_baud_option,
14-
com_timeout_option,
12+
com_option_group,
1513
com_port_argument
1614
)
1715

@@ -53,12 +51,7 @@
5351
help="suppress encoding errors and skip non-ASCII characters",
5452
is_flag=True
5553
)
56-
@option_group(
57-
"Connection options",
58-
"Options related to the serial connection",
59-
com_baud_option(),
60-
com_timeout_option()
61-
)
54+
@com_option_group()
6255
def cli(**kwargs: Any) -> None:
6356
"""Play a Morse encoded ASCII message through the beep signals
6457
of a GeoCom capable total station.

src/instrumentman/morse/app.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ def main(
124124
ignore_non_ascii: bool = False,
125125
baud: int = 9600,
126126
timeout: int = 15,
127+
retry: int = 1,
128+
sync_after_timeout: bool = False,
127129
unittime: int = 50,
128130
compatibility: str = "none",
129131
) -> None:
@@ -134,7 +136,13 @@ def main(
134136
echo_red("The message contains non-ASCII characters.")
135137
exit(1)
136138

137-
with open_serial(port, speed=baud, timeout=timeout) as com:
139+
with open_serial(
140+
port,
141+
speed=baud,
142+
timeout=timeout,
143+
retry=retry,
144+
sync_after_timeout=sync_after_timeout
145+
) as com:
138146
tps = GeoCom(com)
139147
beepstart = tps.bmm.beep_start
140148
beepstop = tps.bmm.beep_stop

0 commit comments

Comments
 (0)