Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 62 additions & 8 deletions aviation/airport.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,25 @@ type Airport struct {
}

type VFRRandomsSpec struct {
Rate int `json:"rate"`
Fleet string `json:"fleet"`
Rate int `json:"rate"`
Fleet string `json:"fleet"`
CommonAircraft []VFRCommonAircraft `json:"common_aircraft,omitempty"`
}

type VFRCommonAircraft struct {
Airline string `json:"airline,omitempty"`
Callsign string `json:"callsign,omitempty"`
Type string `json:"type,omitempty"`
}

type VFRRouteSpec struct {
Name string `json:"name"`
Rate int `json:"rate"`
Fleet string `json:"fleet"`
Waypoints WaypointArray `json:"waypoints"`
Destination string `json:"destination"`
Description string `json:"description"`
Name string `json:"name"`
Rate int `json:"rate"`
Fleet string `json:"fleet"`
CommonAircraft []VFRCommonAircraft `json:"common_aircraft,omitempty"`
Waypoints WaypointArray `json:"waypoints"`
Destination string `json:"destination"`
Description string `json:"description"`
}

type CRDAPair struct {
Expand Down Expand Up @@ -488,6 +496,29 @@ func (ap *Airport) PostDeserialize(icao string, loc Locator, nmPerLongitude floa
e.ErrorString(`"fleet" specified for "vfr" "random_routes" but "rate" is not specified or is zero.`)
}
}
for j, ca := range ap.VFR.Randoms.CommonAircraft {
if ca.Airline == "" && ca.Callsign == "" {
e.ErrorString(`random_routes common_aircraft[%d]: must specify "airline" or "callsign"`, j)
continue
}
if ca.Airline != "" && ca.Callsign != "" {
e.ErrorString(`random_routes common_aircraft[%d]: cannot specify both "airline" and "callsign"`, j)
continue
}
if ca.Callsign != "" && ca.Type == "" {
e.ErrorString(`random_routes common_aircraft[%d]: must specify "type" when "callsign" is set`, j)
}
if ca.Airline != "" {
if _, ok := DB.Airlines[strings.ToUpper(ca.Airline)]; !ok {
e.ErrorString(`random_routes common_aircraft[%d]: airline %q unknown`, j, ca.Airline)
}
}
if ca.Type != "" {
if _, ok := DB.AircraftPerformance[ca.Type]; !ok {
e.ErrorString(`random_routes common_aircraft[%d]: aircraft type %q unknown`, j, ca.Type)
}
}
}
for i := range ap.VFR.Routes {
ap.VFR.Routes[i].Waypoints =
ap.VFR.Routes[i].Waypoints.InitializeLocations(loc, nmPerLongitude, magneticVariation, false, e)
Expand Down Expand Up @@ -520,6 +551,29 @@ func (ap *Airport) PostDeserialize(icao string, loc Locator, nmPerLongitude floa
if _, ok := DB.Airports[spec.Destination]; !ok {
e.ErrorString("Destination airport %q unknown", spec.Destination)
}
for j, ca := range spec.CommonAircraft {
if ca.Airline == "" && ca.Callsign == "" {
e.ErrorString(`common_aircraft[%d]: must specify "airline" or "callsign"`, j)
continue
}
if ca.Airline != "" && ca.Callsign != "" {
e.ErrorString(`common_aircraft[%d]: cannot specify both "airline" and "callsign"`, j)
continue
}
if ca.Callsign != "" && ca.Type == "" {
e.ErrorString(`common_aircraft[%d]: must specify "type" when "callsign" is set`, j)
}
if ca.Airline != "" {
if _, ok := DB.Airlines[strings.ToUpper(ca.Airline)]; !ok {
e.ErrorString(`common_aircraft[%d]: airline %q unknown`, j, ca.Airline)
}
}
if ca.Type != "" {
if _, ok := DB.AircraftPerformance[ca.Type]; !ok {
e.ErrorString(`common_aircraft[%d]: aircraft type %q unknown`, j, ca.Type)
}
}
}
e.Pop()
}
e.Pop()
Expand Down
39 changes: 29 additions & 10 deletions sim/spawn_departures.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,10 +432,9 @@ func (s *Sim) makeNewVFRDeparture(depart string, runway av.RunwayID) (ac *Aircra
s.lg.Errorf("%s: unable to sample VFR destination airport???", depart)
continue
}
ac, _, err = s.createUncontrolledVFRDeparture(depart, arrive, sampledRandoms.Fleet, nil, s.State.SimTime)
ac, _, err = s.createUncontrolledVFRDeparture(depart, arrive, &av.VFRRouteSpec{Fleet: sampledRandoms.Fleet, CommonAircraft: sampledRandoms.CommonAircraft}, s.State.SimTime)
} else if sampledRoute != nil {
ac, _, err = s.createUncontrolledVFRDeparture(depart, sampledRoute.Destination, sampledRoute.Fleet,
sampledRoute.Waypoints, s.State.SimTime)
ac, _, err = s.createUncontrolledVFRDeparture(depart, sampledRoute.Destination, sampledRoute, s.State.SimTime)
}

if err == nil && ac != nil {
Expand Down Expand Up @@ -693,7 +692,7 @@ func (s *Sim) CreateVFRDeparture(departureAirport string) (*Aircraft, error) {
// This shouldn't happen...
return nil, nil
} else {
ac, _, err := s.createUncontrolledVFRDeparture(departureAirport, arrive, ap.VFR.Randoms.Fleet, nil, s.State.SimTime)
ac, _, err := s.createUncontrolledVFRDeparture(departureAirport, arrive, &av.VFRRouteSpec{Fleet: ap.VFR.Randoms.Fleet, CommonAircraft: ap.VFR.Randoms.CommonAircraft}, s.State.SimTime)
return ac, err
}
}
Expand Down Expand Up @@ -724,14 +723,34 @@ func makeDepartureAircraft(ac *Aircraft, simTime Time, model *wx.Model, r *rand.
return d
}

func (s *Sim) createUncontrolledVFRDeparture(depart, arrive, fleet string, routeWps []av.Waypoint, simTime Time) (*Aircraft, string, error) {
func (s *Sim) createUncontrolledVFRDeparture(depart, arrive string, route *av.VFRRouteSpec, simTime Time) (*Aircraft, string, error) {
depap, arrap := av.DB.Airports[depart], av.DB.Airports[arrive]
rwy, _, ok := s.currentVFRRunway(depart)
if !ok {
return nil, "", fmt.Errorf("%s: unable to find current VFR runway", depart)
}

ac, acType := s.sampleAircraft(av.AirlineSpecifier{ICAO: "N", Fleet: fleet}, depart, arrive, s.lg)
var ac *Aircraft
var acType string
if len(route.CommonAircraft) > 0 {
ca := route.CommonAircraft[s.Rand.Intn(len(route.CommonAircraft))]
if ca.Callsign != "" {
callsigns := s.currentCallsigns()
if av.CallsignClashesWithExisting(callsigns, ca.Callsign, s.EnforceUniqueCallsignSuffix) {
return nil, "", fmt.Errorf("callsign %s already in use", ca.Callsign)
}
ac = &Aircraft{ADSBCallsign: av.ADSBCallsign(ca.Callsign), Mode: av.TransponderModeAltitude}
acType = ca.Type
} else {
var types []string
if ca.Type != "" {
types = []string{ca.Type}
}
ac, acType = s.sampleAircraft(av.AirlineSpecifier{ICAO: ca.Airline, AircraftTypes: types}, depart, arrive, s.lg)
}
} else {
ac, acType = s.sampleAircraft(av.AirlineSpecifier{ICAO: "N", Fleet: route.Fleet}, depart, arrive, s.lg)
}
if ac == nil {
return nil, "", fmt.Errorf("unable to sample a valid aircraft")
}
Expand Down Expand Up @@ -781,8 +800,8 @@ func (s *Sim) createUncontrolledVFRDeparture(depart, arrive, fleet string, route

// Fly a downwind if needed
var hdg math.TrueHeading
if len(routeWps) > 0 {
hdg = math.Heading2LL(opp, routeWps[0].Location, s.State.NmPerLongitude)
if len(route.Waypoints) > 0 {
hdg = math.Heading2LL(opp, route.Waypoints[0].Location, s.State.NmPerLongitude)
} else {
hdg = math.Heading2LL(opp, mid, s.State.NmPerLongitude)
}
Expand All @@ -800,8 +819,8 @@ func (s *Sim) createUncontrolledVFRDeparture(depart, arrive, fleet string, route
}

var randomizeAltitudeRange bool
if len(routeWps) > 0 {
wps = append(wps, routeWps...)
if len(route.Waypoints) > 0 {
wps = append(wps, route.Waypoints...)
randomizeAltitudeRange = true
} else {
randomizeAltitudeRange = false
Expand Down
13 changes: 12 additions & 1 deletion sim/spawn_pattern.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,18 @@ func (s *Sim) spawnPatternAircraft() {
var ac *Aircraft
var acType string
for range 20 {
ac, acType = s.sampleAircraft(av.AirlineSpecifier{ICAO: "N", Fleet: ap.VFR.Randoms.Fleet}, name, name, s.lg)
spec := av.AirlineSpecifier{ICAO: "N", Fleet: ap.VFR.Randoms.Fleet}
if len(ap.VFR.Randoms.CommonAircraft) > 0 {
ca := ap.VFR.Randoms.CommonAircraft[s.Rand.Intn(len(ap.VFR.Randoms.CommonAircraft))]
if ca.Callsign == "" {
var types []string
if ca.Type != "" {
types = []string{ca.Type}
}
spec = av.AirlineSpecifier{ICAO: ca.Airline, AircraftTypes: types}
}
}
ac, acType = s.sampleAircraft(spec, name, name, s.lg)
if ac == nil {
continue
}
Expand Down
7 changes: 6 additions & 1 deletion website/facility-engineering.html
Original file line number Diff line number Diff line change
Expand Up @@ -3922,7 +3922,12 @@ <h3 class="section-heading">VFR Aircraft</h3>
<tr><th>Element</th><th>Type</th><th>Description</th></tr>
</thead>
<tbody>

<tr>
<td>"common_aircraft"</td>
<td>Array of objects</td>
<td>(<i>Optional</i>) A list of common callsigns that can be defined for VFR aircraft.
Can include "types"</td>
</tr>
<tr>
<td>"fleet"</td>
<td>String</td>
Expand Down
Loading