Lecture 20 - Short moves; Anthropomorphic Programming.

Public Service Annoucements

1. On Monday the 19th we decided that
• the final exam will start at 19.30 on Friday, 28 July and end at 22.00 on Saturday, 29 July, and
• the final demos will be spread over two days, Wednesday/Thursday, 26/27 July.
2. First Train Control Milestone: Thursday, 29 June.
3. Stalling trains.

Calibration IV

2. Calibrating Constant Velocity

Moving the train at a Pre-chosen Velocity

v = b*v1 + (1-b)*v2

• b is the fraction of time the train runs at speed 1; (1-b) is the fraction of time the train runs at speed 2.
• What assumption is being made about the train's velocity velocity while accelerating and decelerating?

Were the six trains running on the figure eight given velocities like this?

3. Short Moves.

Trains often must travel short distance, starting with the train stopped, and finishing with it stopped. When doing so the train spends its whole time either accelerating or decelerating. Your constant speed calibration is useless because the train doesn't travel at constant speed. Simmilarly your measured stopping distances are not useful.

Creating a perfect calibration of the train's position while it is accelerating is hard. But there is an easy and precise calibration that covers most of the moves the train makes where you need a good calibration It's the subject of this section.

Most of the your train project can get away with ignoring acceleration and decelleration. The one place you can't is when you are doing a short move, giving a speed command followed by a stop command before it gets up to speed. How far will the train go? How long will it be before the train is fully stopped?

Short moves are common when the train is changing direction, which you need to increase the number of possible paths from one point to another.

The general idea is to give the train a carefully timed series of commands knowing how far and for how long the train moves during the series of commands.

4. Calibrating Acceleration and Deceleration: Doing Better

At this point you can do most of the things you will want to do for your project. But some things cannot be done from a standing stop. When one train is following another, for example, it's more elegant to keep the train moving, speeding up and slowing down as required. To do so it's necessary fully to calibrate velocity during the act of accelerating and decelerating. Keeping a train at a pre-determined velocity, for example, requires changing from one speed to another frequently.

To explain velocity changes we must introduce models. On the track the train has a real location, so many cm past sensor S. In your program the train has a position, so many cm past sensor S'. The model is linked to the real train by the calibration. Neither the number of cm nor even the sensor is necessarily the same in the model and in reality because no calibration is perfect. The performance of a project, such as whether trains collide or not, depends on the difference between the model and reality. The remainder of this section is based on minimizing different measures of discrepancies beteen a model and reality.

Back to real trains. When a train driver gives the train a command to change speed, we know roughly how the velocity changes.

1. slowly at first
2. increasing
3. reaching a maximum rate of change, possibly for a non-zero time
4. decreasing
5. more and more slowly as the new velocity is approached
Is this description true of our trains?

How should we model speed changes?

• One might carefully measure the function that gives velocity as a function of time or distance after the command is given. How could one measure the instantaneous velocity?
• Make a video of the train driving beside a tape measure, calculating mm/frame by differencing the positions in successive frames. How sure are you that the interframe time is constant?
• Measure the time it takes for the train to travel at constant speed from one sensor to another. Then give the change speed command at varying distances before the sensor and measure the differences in time to the sensor trigger. Finally, construct a non-linear approximation.
This gives the experimental data that we need to approimate a model.
• Start with the crudest possible approximation, and improve it as you require more precision. An example is below.

The simplest possible model is a step change from the initial velocity to the final velocity. When should the change occur?

• If the train is changing to a higher speed, then the train in the model travels more slowly than the accelerating real train. The real train gets ahead of the train in the model.
• When the step change occurs in the model, the train in the model is travelling faster than the real train. The model starts to catch up to reality.
• The step change in the model is placed so that the train in the model is at the same position as the real train when the velocity change is complete.
• Is the amount that the train in the model falls behind acceptable? If it is not you need a more complex model.
How much does the train in the model fall behind the real train? It depends on what the real train is doing.
• Suppose the real train undergoes constant acceleration.
• The velocity of the real train increases linearly; the step in the model occurs at exactly the mid-point of the change.
• Before the step, the real train travels faster than the model train by
(t - t0) * (v1 - v0) / (t1 - t0)
• The distance the real train gets ahead is
(t - t0)^2 * (v1 - v0) / (2 * (t1 - t0))
• The step occurs at t = t0 + (t1 - t0) / 2 where the real train is ahead by
(t1 - t0) * (v1 - v0) / 8
• Put some typical numbers on this.
• v0 = 50 cm/sec; v1 = 40 cm/sec.
• train travels 1 m: t1 - t0 = -20 sec.
• maximum error is -200 / 8 = -12 cm.
• Whether this is good enough for your project depends on what your project is.

What can we do with a video camera?

Things you might try

1. Use the video camera that's already there on the ceiling. Measure pixels and frames.
2. Use the fiducial marks that have been put along the walls, tracks and ceiling.
3. Use something better than video. e.g. Arduino

Anthropomorphic Programming

We all, even most programmers (!), have effective intuitions about human relations

• We use them to `understand' pets, which means attributing to them
• goals
• knowledge
• capabilities
• emotions
• Why not programs?
• apply them to intertask relationships

• Understand them by thinking about them as if they have capabilities and goals.
• When you are developing something like the train application you are defining roles and relationships

Why do servers need attendant tasks?

• What happens if a server calls AwaitEvent?

1. Proprietor with a Notifier

Proprietor `owns' a service, which usually means a resource.

• Think of the owner at the counter of an old-fashioned store
• `store' means where things are stored;
• a proprietor is a store-keeper;
• The goods are behind the counter and only the proprietor can access them.
• Clients come to the front and are processed one by one.
• Comment. The modern `store' is considered by many to be the most important innovation of the 20th century. (Yes, including the transistor, the computer, quantum mechanics, antibiotics, etc.) A whole lot of work that was previously done by store personnel is now done by the client. This is possible only because extensive codes of conduct have been internalized by clients. (That is, a large collection of new behaviour norms have been created and propagated.)
• Somebody has to sit out back waiting for the truck and bringing it to the proprietor

Notes

1. Notifier is usually of higher priority than server
• Notice the early reply in the proprietor
2. The server buffers both clients and data from the notifier. In this implementation client and data buffering are duals of each other. Our server code should
• pack input into (unpack output from) logical units,
• exhibit duality explicitly,
• be easy to break into parts, and
• be easy to extend.

2. Using a Courier

We can handle two interrupts coming very close together using a courier. Simplest is best, so we shouldn't go beyond a courier unless we expect more than two.

Transmit Notifier Code

• Initialize
```      Receive( &courierTid, ... );
```
• Work
```      FOREVER {
store( UART..., byte )
data = AwaitEvent( eventid );
}
```

Transmit Courier Code

• Initialize
```      Receive( &serverTid, notifierTid );
Send( notifierTid, ... );
```
• Work
```      FOREVER {
Send( notifierTid, {data} );
Send( serverTid, {req}, {data} );
}
```

Transmit Proprietor Code

• Initialize
```      // queues & fifos
notifierTid = Create( notifier );
courierTid = Create( courier );
Send( courierTid, notifierTid, ... ); // On return courier & notifier are known to be okay
RegisterAs( );                        //On return client requests will begin.
```
• Work
```      FOREVER {
requesterTid = Receive( request, {request-type, data} );
switch ( request-type ) {
case NOT_XMIT:
enqueue( requesterTid, xmitQ )
if ( ! empty( xmitFifo ) ) Reply( dequeue( xmitQ ), dequeue( xmitFifo ) );
break;
case CLIENT_XMIT:
enqueue ( xmitFifo, data );
if ( ! empty( xmitQ ) ) Reply( dequeue( xmitQ ), dequeue( xmitFifo ) );
break;
default:
ASSERT( "..." );
}
}
```

Notes

This gets you through a bottleneck where at most than two events come too fast.

Remember that all the calls provide error returns. You can/should use them for error recovery

• static error recovery: debugging
• dynamic error recovery: at run time

Another possible arrangement for task creation

• Server creates the courier
• Couier creates the notifier

Another possible arrangement for initialization

• Courier sends to its parentTid
• Notifier sends to its parentTid

Distributed gating

I am showing you collections of tasks implemented together because sets of related tasks is a level of organization above the individual task.

E.g., the decision to add a courier requires revision of code within the group, but not outside it.

3. The Warehouse

Add a warehouse between the courier and the notifier.

• In structure the warehouse is a typical server.
• The warehouse has only a single client, the courier, so its sendQ is never longer than one.
• The warehouse put together multiple inputs into a package, or break a package into multiple outputs.
• The sooner you package, and the later you unpackage, the fewer messages you need to pass.

Initialization

The initialization given for the courier, above, generalizes to include the warehouse, essentially without change.

Forever

The notifier is now talking directly to a server and has the shape given above for the proprietor. The warehouse cannot talk directly to the proprietor because both are servers.

• A courier is inserted between them.
• If there is to be two way traffic between the proprietor and the warehouse two servers are needed.

Note

This structure clears up most problems when a burst of requests to the server would leave the notifier waiting in a long sendQ.

• Warehouse and proprietor share the work.
• Server's Tid is public; Warehouse's Tid is private.
• This is far from the only way to share the work. For example,
• The server could be guarded by a receptionist (assistant) who ensures that another client request occurs only when the previous request is complete. Then the warehouse is unnecessary.

Note:

1. Handles bottlenecks of all sizes. Give a precise and quantitative definition of `bottleneck'.

What this amounts to is that a server should be lean and hungry

• do as little as possible