예제 #1
0
    def _update_obj(self, entity: Entity,
                    state: PhysicsState, origin: Entity) -> None:
        # update planet objects
        self._obj.pos = entity.screen_pos(origin) / self._scale_factor
        self._obj.axis = calc.angle_to_vpy(entity.heading)

        # update label objects
        self._label.text = self._label_text(entity)
        self._label.pos = entity.screen_pos(origin) / self._scale_factor
예제 #2
0
    def _update_obj(self, entity: Entity, state: PhysicsState,
                    origin: Entity) -> None:
        # update planet objects
        self._obj.pos = entity.screen_pos(origin)
        self._obj.axis = calc.angle_to_vpy(entity.heading)

        # update label objects
        self._label.text = self._label_text(entity)
        self._label.pos = entity.screen_pos(origin)
        # update landing graphic objects
        self._update_landing_graphic(self._small_landing_graphic, entity,
                                     state.craft_entity())
        self._update_landing_graphic(self._large_landing_graphic, entity,
                                     state.craft_entity())
예제 #3
0
파일: habitat.py 프로젝트: OCESS/orbitx
    def _create_obj(self, entity: Entity, origin: Entity,
                    texture: Optional[str]) -> vpython.compound:
        """Creates the habitat, and also a new minimap scene and habitat."""
        assert texture is not None
        habitat = self._create_hab(entity, texture)
        habitat.pos = entity.screen_pos(origin)

        main_scene = vpython.canvas.get_selected()
        self._minimap_canvas = vpython.canvas(width=200,
                                              height=150,
                                              userspin=False,
                                              userzoom=False,
                                              up=common.DEFAULT_UP,
                                              forward=common.DEFAULT_FORWARD)
        self._minimap_canvas.append_to_caption(
            # The element styling will be removed at runtime. This just hides
            # this helptext during startup.
            "<span class='helptext' style='display: none'>"
            "This small 'minimap' shows the Habitat's orientation. The red "
            "arrow represents the Hab's velocity relative to the reference, "
            "and the gray arrow points to position of the reference."
            "</span>")

        self._small_habitat = self._create_hab(entity, texture)
        self._ref_arrow = vpython.arrow(color=vpython.color.gray(0.5))
        self._velocity_arrow = vpython.arrow(color=vpython.color.red)
        main_scene.select()

        self._broken: bool = False

        return habitat
예제 #4
0
    def _create_obj(self, entity: Entity, origin: Entity,
                    texture: Optional[str]) -> vpython.sphere:

        assert texture is not None
        earth = self._create_earth(entity, texture)
        earth.pos = entity.screen_pos(origin)
        earth.clouds.pos = earth.pos

        return earth
예제 #5
0
파일: habitat.py 프로젝트: OCESS/orbitx
 def _label_text(self, entity: Entity) -> str:
     label = entity.name
     if entity.broken:
         label += ' [BROKEN]'
     label += '\nFuel: ' + common.format_num(entity.fuel, " kg")
     if entity.landed_on == AYSE:
         label += '\nDocked'
     elif entity.landed():
         label += '\nLanded'
     return label
예제 #6
0
파일: habitat.py 프로젝트: jonahhw/orbitx
 def _label_text(self, entity: Entity, state: PhysicsState) -> str:
     label = super()._label_text(entity, state)
     if entity.broken:
         label += ' [BROKEN]'
     label += '\nFuel: ' + common.format_num(entity.fuel, " kg")
     if entity.landed_on == common.AYSE:
         label += '\nDocked'
     elif entity.landed():
         label += '\nLanded'
     return label
예제 #7
0
 def _create_obj(self, entity: Entity, origin: Entity,
                 texture: Optional[str]) -> vpython.sphere:
     return vpython.sphere(pos=entity.screen_pos(origin),
                           axis=calc.angle_to_vpy(entity.heading),
                           up=DEFAULT_UP,
                           forward=DEFAULT_FORWARD,
                           radius=entity.r,
                           make_trail=True,
                           retain=10000,
                           texture=texture,
                           bumpmap=vpython.bumpmaps.gravel,
                           shininess=Planet.SHININIESS)
예제 #8
0
파일: planet.py 프로젝트: pmelanson/orbitx
 def _create_obj(self, entity: Entity, origin: Entity,
                 texture: Optional[str]) -> vpython.sphere:
     return vpython.sphere(
         pos=entity.screen_pos(origin),
         axis=calc.angle_to_vpy(entity.heading),
         up=vpython.vector(0, 0, 1),
         # So the planet doesn't intersect the landing graphic
         radius=entity.r * 0.9,
         make_trail=True,
         retain=10000,
         texture=texture,
         bumpmap=vpython.bumpmaps.gravel,
         shininess=Planet.SHININIESS)
예제 #9
0
    def _create_obj(self, entity: Entity, origin: Entity,
                    texture: Optional[str]) -> vpython.sphere:
        main_body = vpython.box()
        side_panels = vpython.box(height=2, width=0.5, length=0.6)
        obj = vpython.compound([main_body, side_panels],
                               make_trail=True,
                               texture=texture,
                               bumpmap=vpython.textures.gravel)
        obj.pos = entity.screen_pos(origin)
        obj.axis = calc.angle_to_vpy(entity.heading)
        obj.length = entity.r * 2
        obj.height = entity.r * 2
        obj.width = entity.r * 2

        # A compound object doesn't actually have a radius, but we need to
        # monkey-patch this for when we recentre the camera, to determine the
        # relevant_range of the space station
        obj.radius = entity.r
        return obj
예제 #10
0
    def test_fields(self):
        def test_field(pe: Entity, field: str, val):
            pe.proto.Clear()
            setattr(pe, field, val)
            self.assertEqual(getattr(pe.proto, field), val)

        pe = Entity(protos.Entity())
        test_field(pe, 'name', 'test')
        test_field(pe, 'x', 5)
        test_field(pe, 'y', 5)
        test_field(pe, 'vx', 5)
        test_field(pe, 'vy', 5)
        test_field(pe, 'r', 5)
        test_field(pe, 'mass', 5)
        test_field(pe, 'heading', 5)
        test_field(pe, 'spin', 5)
        test_field(pe, 'fuel', 5)
        test_field(pe, 'throttle', 5)
        test_field(pe, 'landed_on', 'other_test')
        test_field(pe, 'broken', True)
        test_field(pe, 'artificial', True)
예제 #11
0
    def _create_obj(self, entity: Entity, origin: Entity,
                    texture_path: Optional[str]) -> vpython.sphere:
        ship = vpython.cone(pos=vpython.vector(5, 0, 0),
                            axis=vpython.vector(5, 0, 0),
                            radius=3)
        entrance = vpython.extrusion(
            path=[vpython.vec(0, 0, 0),
                  vpython.vec(4, 0, 0)],
            shape=[
                vpython.shapes.circle(radius=3),
                vpython.shapes.rectangle(pos=[0, 0], width=0.5, height=0.5)
            ],
            pos=vpython.vec(3, 0, 0))

        docking_arm = vpython.extrusion(
            path=[
                vpython.vec(0, 0, 0),
                vpython.vec(1.5, 0, 0),
                vpython.vec(1.5, 0.5, 0)
            ],
            shape=[vpython.shapes.circle(radius=0.03)])

        obj = vpython.compound([ship, entrance, docking_arm],
                               make_trail=True,
                               texture=vpython.textures.metal,
                               bumpmap=vpython.bumpmaps.gravel)
        obj.pos = entity.screen_pos(origin)
        obj.axis = calc.angle_to_vpy(entity.heading)
        obj.length = entity.r * 2
        obj.height = entity.r * 2
        obj.width = entity.r * 2

        # A compound object doesn't actually have a radius, but we need to
        # monkey-patch this for when we recentre the camera, to determine the
        # relevant_range of the space station
        obj.radius = entity.r

        return obj
예제 #12
0
파일: engine.py 프로젝트: Arcayik/orbitx
def _one_request(request: Request, y0: PhysicsState) \
        -> PhysicsState:
    """Interface to set habitat controls.

    Use an argument to change habitat throttle or spinning, and simulation
    will restart with this new information."""
    log.info(f'At simtime={y0.timestamp}, '
             f'Got command {MessageToString(request, as_one_line=True)}')

    if request.ident != Request.TIME_ACC_SET:
        # Reveal the type of y0.craft as str (not None).
        assert y0.craft is not None

    if request.ident == Request.HAB_SPIN_CHANGE:
        if y0.navmode != Navmode['Manual']:
            # We're in autopilot, ignore this command
            return y0
        craft = y0.craft_entity()
        if not craft.landed():
            craft.spin += request.spin_change
    elif request.ident == Request.HAB_THROTTLE_CHANGE:
        y0.craft_entity().throttle += request.throttle_change
    elif request.ident == Request.HAB_THROTTLE_SET:
        y0.craft_entity().throttle = request.throttle_set
    elif request.ident == Request.TIME_ACC_SET:
        assert request.time_acc_set >= 0
        y0.time_acc = request.time_acc_set
    elif request.ident == Request.ENGINEERING_UPDATE:
        # Multiply this value by 100, because OrbitV considers engines at
        # 100% to be 100x the maximum thrust.
        common.craft_capabilities[HABITAT] = \
            common.craft_capabilities[HABITAT]._replace(
                thrust=100 * request.engineering_update.max_thrust)
        hab = y0[HABITAT]
        ayse = y0[AYSE]
        hab.fuel = request.engineering_update.hab_fuel
        ayse.fuel = request.engineering_update.ayse_fuel
        y0[HABITAT] = hab
        y0[AYSE] = ayse

        if request.engineering_update.module_state == \
                Request.DETACHED_MODULE and \
                MODULE not in y0._entity_names and \
                not hab.landed():
            # If the Habitat is freely floating and engineering asks us to
            # detach the Module, spawn in the Module.
            module = Entity(protos.Entity(
                name=MODULE, mass=100, r=10, artificial=True))
            module.pos = (hab.pos - (module.r + hab.r) *
                          calc.heading_vector(hab.heading))
            module.v = calc.rotational_speed(module, hab)

            y0_proto = y0.as_proto()
            y0_proto.entities.extend([module.proto])
            y0 = PhysicsState(None, y0_proto)

    elif request.ident == Request.UNDOCK:
        habitat = y0[HABITAT]

        if habitat.landed_on == AYSE:
            ayse = y0[AYSE]
            habitat.landed_on = ''

            norm = habitat.pos - ayse.pos
            unit_norm = norm / calc.fastnorm(norm)
            habitat.v += unit_norm * common.UNDOCK_PUSH
            habitat.spin = ayse.spin

            y0[HABITAT] = habitat

    elif request.ident == Request.REFERENCE_UPDATE:
        y0.reference = request.reference
    elif request.ident == Request.TARGET_UPDATE:
        y0.target = request.target
    elif request.ident == Request.LOAD_SAVEFILE:
        y0 = common.load_savefile(common.savefile(request.loadfile))
    elif request.ident == Request.NAVMODE_SET:
        y0.navmode = Navmode(request.navmode)
        if y0.navmode == Navmode['Manual']:
            y0.craft_entity().spin = 0
    elif request.ident == Request.PARACHUTE:
        y0.parachute_deployed = request.deploy_parachute
    elif request.ident == Request.IGNITE_SRBS:
        if round(y0.srb_time) == common.SRB_FULL:
            y0.srb_time = common.SRB_BURNTIME
    elif request.ident == Request.TOGGLE_COMPONENT:
        component = y0.engineering.components[request.component_to_toggle]
        component.connected = not component.connected
    elif request.ident == Request.TOGGLE_RADIATOR:
        radiator = y0.engineering.radiators[request.radiator_to_toggle]
        radiator.functioning = not radiator.functioning
    elif request.ident == Request.CONNECT_RADIATOR_TO_LOOP:
        radiator = y0.engineering.radiators[request.radiator_to_loop.rad]
        radiator.attached_to_coolant_loop = request.radiator_to_loop.loop
    elif request.ident == Request.TOGGLE_COMPONENT_COOLANT:
        component = y0.engineering.components[request.component_to_loop.component]
        # See comments on _component_coolant_cnxn_transitions for more info.
        component.coolant_connection = (
            _component_coolant_cnxn_transitions
            [request.component_to_loop.loop]
            [component.coolant_connection]
        )

    return y0
예제 #13
0
파일: habitat.py 프로젝트: OCESS/orbitx
    def _update_obj(self, entity: Entity, state: PhysicsState, origin: Entity):
        super()._update_obj(entity, state, origin)
        self._obj.boosters.pos = self._obj.pos
        self._obj.boosters.axis = self._obj.axis
        # Attach the parachute to the forward cone of the habitat.
        self._obj.parachute.pos = (
            self._obj.pos + calc.angle_to_vpy(entity.heading) * entity.r * 0.8)

        parachute_is_visible = ((state.craft == entity.name)
                                and state.parachute_deployed)
        if parachute_is_visible:
            drag = calc.drag(state)
            drag_mag = np.inner(drag, drag)
        for parachute in [self._obj.parachute, self._small_habitat.parachute]:
            if not parachute_is_visible or drag_mag < 0.00001:
                # If parachute_is_visible == False, don't show the parachute.
                # If the drag is basically zero, don't show the parachute.
                parachute.visible = False
                continue

            if drag_mag > 0.1:
                parachute.width = self.PARACHUTE_RADIUS * 2
                parachute.height = self.PARACHUTE_RADIUS * 2
            else:
                # Below a certain threshold the parachute deflates.
                parachute.width = self.PARACHUTE_RADIUS
                parachute.height = self.PARACHUTE_RADIUS

            parachute.axis = -vpython.vec(*drag, 0)
            parachute.visible = True

        if not self._broken and entity.broken:
            # We weren't broken before, but looking at new data we realize
            # we're now broken. Change the habitat texture.
            new_texture = self._obj.texture.replace('Habitat.jpg',
                                                    'Habitat-broken.jpg')
            assert new_texture != self._obj.texture, \
                f'{new_texture!r} == {self._obj.texture!r}'
            self._obj.texture = new_texture
            self._small_habitat.texture = new_texture
            self._broken = entity.broken
        elif self._broken and not entity.broken:
            # We were broken before, but we've repaired ourselves somehow.
            new_texture = self._obj.texture.replace('Habitat-broken.jpg',
                                                    'Habitat.jpg')
            assert new_texture != self._obj.texture, \
                f'{new_texture!r} == {self._obj.texture!r}'
            self._obj.texture = new_texture
            self._small_habitat.texture = new_texture
            self._broken = entity.broken

        # Set reference and target arrows of the minimap habitat.
        same = state.reference == entity.name
        default = vpython.vector(0, 0, -1)

        ref_arrow_axis = (entity.screen_pos(state.reference_entity()).norm() *
                          entity.r * -1.2)
        v = entity.v - state.reference_entity().v
        velocity_arrow_axis = \
            vpython.vector(*v, 0).norm() * entity.r

        self._ref_arrow.axis = default if same else ref_arrow_axis
        self._velocity_arrow.axis = default if same else velocity_arrow_axis
        self._small_habitat.axis = self._obj.axis
        self._small_habitat.boosters.axis = self._obj.axis

        # Invisible-ize the SRBs if we ran out.
        if state.srb_time == common.SRB_EMPTY and self._obj.boosters.visible:
            self._obj.boosters.visible = False
            self._small_habitat.boosters.visible = False
예제 #14
0
 def _label_text(self, entity: Entity, state: PhysicsState) -> str:
     label = super()._label_text(entity, state)
     label += '\nFuel: ' + common.format_num(entity.fuel, " kg")
     if entity.landed():
         label += '\nLanded'
     return label
예제 #15
0
 def _label_text(self, entity: Entity) -> str:
     return (
             f'{entity.name}\n'
             f'Fuel: {common.format_num(entity.fuel, " kg")}' +
             ('\nLanded' if entity.landed() else '')
     )
예제 #16
0
def clone_orbitv_state(rnd_path: Path) -> PhysicsState:
    """Gathers information from an OrbitV savefile, in the *.RND format,
    as well as a STARSr file.
    Returns a PhysicsState representing everything we can read."""
    proto_state = protos.PhysicalState()
    starsr_path = rnd_path.parent / 'STARSr'

    log.info(f"Reading OrbitV file {rnd_path}, "
             f"using info from adjacent STARSr file {starsr_path}")

    hab_index: Optional[int] = None
    ayse_index: Optional[int] = None

    with open(starsr_path, 'r') as starsr_file:
        starsr = csv.reader(starsr_file, delimiter=',')

        background_star_line = next(starsr)
        while len(background_star_line) == 3:
            background_star_line = next(starsr)

        # After the last while loop, the value of background_star_line is
        # actually a gravity_pair line (a pair of indices, which we also don't
        # care about).
        gravity_pair_line = background_star_line
        while len(gravity_pair_line) == 2:
            gravity_pair_line = next(starsr)

        entity_constants_line = gravity_pair_line
        while len(entity_constants_line) == 6:
            proto_entity = proto_state.entities.add()

            # Cast all the elements on a line to floats.
            # Some strings will be the form '1.234D+04', convert these too.
            (colour, mass, radius,
                atmosphere_thickness, atmosphere_scaling, atmosphere_height) \
                = map(_string_to_float, entity_constants_line)
            mass = max(1, mass)

            proto_entity.mass = mass
            proto_entity.r = radius
            if atmosphere_thickness and atmosphere_scaling:
                # Only set atmosphere fields if they both have a value.
                proto_entity.atmosphere_thickness = atmosphere_thickness
                proto_entity.atmosphere_scaling = atmosphere_scaling

            entity_constants_line = next(starsr)

        # We skip a line here. It's the timestamp line, but the .RND file will
        # have more up-to-date timestamp info.
        entity_positions_line = next(starsr)

        # We don't care about entity positions, we'll get this from the .RND
        # file as well.
        while len(entity_positions_line) == 6:
            entity_positions_line = next(starsr)

        entity_name_line = entity_positions_line
        entity_index = 0
        while len(entity_name_line) == 1:
            name = entity_name_line[0].strip()
            proto_state.entities[entity_index].name = name

            if name == common.HABITAT:
                hab_index = entity_index
            elif name == common.AYSE:
                ayse_index = entity_index

            entity_index += 1
            entity_name_line = next(starsr)

    assert proto_state.entities[0].name == common.SUN, (
        'We assume that the Sun is the first OrbitV entity, this has '
        'implications for how we populate entity positions.')
    assert hab_index is not None, \
        "We didn't see the Habitat in the STARSr file!"
    assert ayse_index is not None, \
        "We didn't see AYSE in the STARSr file!"

    hab = proto_state.entities[hab_index]
    ayse = proto_state.entities[ayse_index]

    with open(rnd_path, 'rb') as rnd:
        check_byte = rnd.read(1)
        rnd.read(len("ORBIT5S        "))
        craft_throttle = _read_single(rnd) / 100

        # I don't know what Vflag and Aflag do.
        _read_int(rnd), _read_int(rnd)
        navmode = Navmode(SFLAG_TO_NAVMODE[_read_int(rnd)])
        if navmode is not None:
            proto_state.navmode = navmode.value

        _read_double(rnd)  # This is drag, we calculate this ourselves.
        _read_double(rnd)  # This is zoom, we ignore this as well.
        hab.heading = _read_single(rnd)
        # Skip centre, ref, and targ information.
        _read_int(rnd), _read_int(rnd), _read_int(rnd)

        # We don't track if trails are set.
        _read_int(rnd)
        drag_profile = _read_single(rnd)
        proto_state.parachute_deployed = (drag_profile > 0.002)

        current_srb_output = _read_single(rnd)
        if current_srb_output != 0:
            proto_state.srb_time = common.SRB_BURNTIME

        # We don't care about colour trails or how times are displayed.
        _read_int(rnd), _read_int(rnd)
        _read_double(rnd), _read_double(rnd)  # No time acc info either.
        _read_single(rnd)  # Ignore some RCPS info
        _read_int(rnd)  # Eflag? Something that gassimv.bas sets.

        year = _read_int(rnd)
        day = _read_int(rnd)
        hour = _read_int(rnd)
        minute = _read_int(rnd)
        second = _read_double(rnd)

        timestamp = datetime(
            year, 1, 1, hour=hour, minute=minute,
            second=int(second)) + timedelta(day - 1)
        proto_state.timestamp = timestamp.timestamp()

        ayse.heading = _read_single(rnd)
        _read_int(rnd)  # AYSEscrape seems to do nothing.

        # TODO: Once we implement wind, fill in these fields.
        _read_single(rnd), _read_single(rnd)

        hab.spin = numpy.radians(_read_single(rnd))
        docked_constant = _read_int(rnd)
        if docked_constant != 0:
            hab.landed_on = common.AYSE

        _read_single(rnd)  # Rad shields, overwritten by enghabv.bas.
        # TODO: once module is implemented, have it be launched here.
        _read_int(rnd)
        # module_state = MODFLAG_TO_MODSTATE[modflag]

        _read_single(rnd), _read_single(rnd)  # AYSEdist and OCESSdist.

        hab.broken = bool(_read_int(rnd))
        ayse.broken = bool(_read_int(rnd))

        # TODO: Once we implement nav malfunctions, set this field.
        _read_single(rnd)

        # Max thrust, ignored because we'll read it from ORBITSSE.rnd
        _read_single(rnd)

        # TODO: Once we implement nav malfunctions, set this field.
        # Also, apparently this is the same field as two fields ago?
        _read_single(rnd)

        # TODO: Once we implement wind, fill in these fields.
        _read_long(rnd)

        # TODO: There is some information required from MST.rnd to implement
        # this field (LONGtarg).
        _read_single(rnd)

        # We calculate pressure and agrav.
        _read_single(rnd), _read_single(rnd)
        # Note, we skip the first entity, the Sun, since OrbitV does.
        for i in range(1, min(39, len(proto_state.entities))):
            entity = proto_state.entities[i]
            entity.x, entity.y, entity.vx, entity.vy = (_read_double(rnd),
                                                        _read_double(rnd),
                                                        _read_double(rnd),
                                                        _read_double(rnd))

        hab.fuel = _read_single(rnd)
        ayse.fuel = _read_single(rnd)

        check_byte_2 = rnd.read(1)

    if check_byte != check_byte_2:
        log.warning('RND file had inconsistent check bytes: '
                    f'{check_byte} != {check_byte_2}')

    # Delete any entity with a (0, 0) position that isn't the Sun.
    # TODO: We also delete the OCESS launch tower, but once we implement
    # OCESS we should no longer do this.
    entity_index = 0
    while entity_index < len(proto_state.entities):
        proto_entity = proto_state.entities[entity_index]
        if round(proto_entity.x) == 0 and round(proto_entity.y) == 0 and \
                proto_entity.name != common.SUN or \
                proto_entity.name == common.OCESS:
            del proto_state.entities[entity_index]
        else:
            entity_index += 1

    hab.artificial = True
    ayse.artificial = True
    # Habitat and AYSE masses are hardcoded in OrbitV.
    hab.mass = 275000
    ayse.mass = 20000000

    orbitx_state = PhysicsState(None, proto_state)
    orbitx_state[common.HABITAT] = Entity(hab)
    orbitx_state[common.AYSE] = Entity(ayse)

    craft = orbitx_state.craft_entity()
    craft.throttle = craft_throttle

    log.debug(f'Interpreted {rnd_path} and {starsr_path} into this state:')
    log.debug(repr(orbitx_state))
    orbitx_state = _separate_landed_entities(orbitx_state)
    return orbitx_state