mirror of
https://github.com/TomHarte/CLK.git
synced 2025-04-09 15:39:08 +00:00
Merge pull request #43 from TomHarte/NonSkippingTracks
Sought to prevent a reset of angular position when changing tracks
This commit is contained in:
commit
b46355331c
@ -29,6 +29,8 @@ unsigned int NumberTheory::greatest_common_divisor(unsigned int a, unsigned int
|
||||
|
||||
unsigned int NumberTheory::least_common_multiple(unsigned int a, unsigned int b)
|
||||
{
|
||||
if(a == b) return a;
|
||||
|
||||
unsigned int gcd = greatest_common_divisor(a, b);
|
||||
return (a / gcd) * (b / gcd);
|
||||
return (a / gcd) * (b / gcd) * gcd;
|
||||
}
|
||||
|
@ -37,9 +37,16 @@ class Track {
|
||||
};
|
||||
|
||||
/*!
|
||||
Returns the next event that will be detected during rotation of this disk.
|
||||
@returns the next event that will be detected during rotation of this disk.
|
||||
*/
|
||||
virtual Event get_next_event() = 0;
|
||||
|
||||
/*!
|
||||
Jumps to the event latest offset that is less than or equal to the input time.
|
||||
|
||||
@returns the time jumped to.
|
||||
*/
|
||||
virtual Time seek_to(Time time_since_index_hole) = 0;
|
||||
};
|
||||
|
||||
/*!
|
||||
|
@ -17,7 +17,11 @@ DiskDrive::DiskDrive(unsigned int clock_rate, unsigned int clock_rate_multiplier
|
||||
_head_position(0),
|
||||
|
||||
TimedEventLoop(clock_rate * clock_rate_multiplier)
|
||||
{}
|
||||
{
|
||||
_rotational_multiplier.length = 60;
|
||||
_rotational_multiplier.clock_rate = revolutions_per_minute;
|
||||
_rotational_multiplier.simplify();
|
||||
}
|
||||
|
||||
void DiskDrive::set_expected_bit_length(Time bit_length)
|
||||
{
|
||||
@ -33,7 +37,7 @@ void DiskDrive::set_expected_bit_length(Time bit_length)
|
||||
void DiskDrive::set_disk(std::shared_ptr<Disk> disk)
|
||||
{
|
||||
_disk = disk;
|
||||
set_track();
|
||||
set_track(Time());
|
||||
}
|
||||
|
||||
bool DiskDrive::has_disk()
|
||||
@ -49,14 +53,32 @@ bool DiskDrive::get_is_track_zero()
|
||||
void DiskDrive::step(int direction)
|
||||
{
|
||||
_head_position = std::max(_head_position + direction, 0);
|
||||
set_track();
|
||||
Time extra_time = get_time_into_next_event() / _rotational_multiplier;
|
||||
extra_time.simplify();
|
||||
_time_into_track += extra_time;
|
||||
set_track(_time_into_track);
|
||||
}
|
||||
|
||||
void DiskDrive::set_track()
|
||||
void DiskDrive::set_track(Time initial_offset)
|
||||
{
|
||||
_track = _disk->get_track_at_position((unsigned int)_head_position);
|
||||
// TODO: probably a better implementation of the empty track?
|
||||
Time offset;
|
||||
if(_track && _time_into_track.length > 0)
|
||||
{
|
||||
Time time_found = _track->seek_to(_time_into_track).simplify();
|
||||
offset = (_time_into_track - time_found).simplify();
|
||||
_time_into_track = time_found;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = _time_into_track;
|
||||
_time_into_track.set_zero();
|
||||
}
|
||||
|
||||
reset_timer();
|
||||
get_next_event();
|
||||
reset_timer_to_offset(offset * _rotational_multiplier);
|
||||
}
|
||||
|
||||
void DiskDrive::run_for_cycles(int number_of_cycles)
|
||||
@ -88,11 +110,7 @@ void DiskDrive::get_next_event()
|
||||
|
||||
// divide interval, which is in terms of a rotation of the disk, by rotation speed, and
|
||||
// convert it into revolutions per second
|
||||
Time event_interval = _current_event.length;
|
||||
event_interval.length *= 60;
|
||||
event_interval.clock_rate *= _revolutions_per_minute;
|
||||
event_interval.simplify();
|
||||
set_next_event_time_interval(event_interval);
|
||||
set_next_event_time_interval(_current_event.length * _rotational_multiplier);
|
||||
}
|
||||
|
||||
void DiskDrive::process_next_event()
|
||||
@ -101,9 +119,11 @@ void DiskDrive::process_next_event()
|
||||
{
|
||||
case Track::Event::FluxTransition:
|
||||
_pll->add_pulse();
|
||||
_time_into_track += _current_event.length;
|
||||
break;
|
||||
case Track::Event::IndexHole:
|
||||
_cycles_since_index_hole = 0;
|
||||
_time_into_track.set_zero();
|
||||
process_index_hole();
|
||||
break;
|
||||
}
|
||||
|
@ -93,16 +93,18 @@ class DiskDrive: public DigitalPhaseLockedLoop::Delegate, public TimedEventLoop
|
||||
unsigned int _clock_rate;
|
||||
unsigned int _clock_rate_multiplier;
|
||||
unsigned int _revolutions_per_minute;
|
||||
Time _rotational_multiplier;
|
||||
|
||||
std::shared_ptr<DigitalPhaseLockedLoop> _pll;
|
||||
std::shared_ptr<Disk> _disk;
|
||||
std::shared_ptr<Track> _track;
|
||||
int _head_position;
|
||||
unsigned int _cycles_since_index_hole;
|
||||
void set_track();
|
||||
void set_track(Time initial_offset);
|
||||
|
||||
inline void get_next_event();
|
||||
Track::Event _current_event;
|
||||
Time _time_into_track;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -61,6 +61,45 @@ PCMTrack::Event PCMTrack::get_next_event()
|
||||
return _next_event;
|
||||
}
|
||||
|
||||
Time PCMTrack::seek_to(Time time_since_index_hole)
|
||||
{
|
||||
_segment_pointer = 0;
|
||||
|
||||
// pick a common clock rate for counting time on this track and multiply up the time being sought appropriately
|
||||
Time time_so_far;
|
||||
time_so_far.clock_rate = NumberTheory::least_common_multiple(_next_event.length.clock_rate, time_since_index_hole.clock_rate);
|
||||
time_since_index_hole.length *= time_so_far.clock_rate / time_since_index_hole.clock_rate;
|
||||
time_since_index_hole.clock_rate = time_so_far.clock_rate;
|
||||
|
||||
while(_segment_pointer < _segments.size())
|
||||
{
|
||||
// determine how long this segment is in terms of the master clock
|
||||
unsigned int clock_multiplier = time_so_far.clock_rate / _next_event.length.clock_rate;
|
||||
unsigned int bit_length = ((clock_multiplier / _track_clock_rate) / _segments[_segment_pointer].length_of_a_bit.clock_rate) * _segments[_segment_pointer].length_of_a_bit.length;
|
||||
unsigned int time_in_this_segment = bit_length * _segments[_segment_pointer].number_of_bits;
|
||||
|
||||
// if this segment goes on longer than the time being sought, end here
|
||||
unsigned int time_remaining = time_since_index_hole.length - time_so_far.length;
|
||||
if(time_in_this_segment >= time_remaining)
|
||||
{
|
||||
// get the amount of time actually to move into this segment
|
||||
unsigned int time_found = time_remaining - (time_remaining % bit_length);
|
||||
|
||||
// resolve that into the stateful bit count
|
||||
_bit_pointer = 1 + (time_remaining / bit_length);
|
||||
|
||||
// update and return the time sought to
|
||||
time_so_far.length += time_found;
|
||||
return time_so_far;
|
||||
}
|
||||
|
||||
// otherwise, accumulate time and keep moving
|
||||
time_so_far.length += time_in_this_segment;
|
||||
_segment_pointer++;
|
||||
}
|
||||
return time_since_index_hole;
|
||||
}
|
||||
|
||||
void PCMTrack::fix_length()
|
||||
{
|
||||
// find the least common multiple of all segment clock rates
|
||||
|
@ -47,6 +47,7 @@ class PCMTrack: public Track {
|
||||
|
||||
// as per @c Track
|
||||
Event get_next_event();
|
||||
Time seek_to(Time time_since_index_hole);
|
||||
|
||||
private:
|
||||
// storage for the segments that describe this track
|
||||
|
@ -19,27 +19,109 @@ namespace Storage {
|
||||
*/
|
||||
struct Time {
|
||||
unsigned int length, clock_rate;
|
||||
Time() : length(0), clock_rate(1) {}
|
||||
|
||||
/*!
|
||||
Reduces this @c Time to its simplest form — eliminates all common factors from @c length
|
||||
and @c clock_rate.
|
||||
*/
|
||||
inline void simplify()
|
||||
inline Time &simplify()
|
||||
{
|
||||
unsigned int common_divisor = NumberTheory::greatest_common_divisor(length, clock_rate);
|
||||
length /= common_divisor;
|
||||
clock_rate /= common_divisor;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the floating point conversion of this @c Time. This will often be less precise.
|
||||
@returns the floating point conversion of this @c Time. This will often be less precise.
|
||||
*/
|
||||
inline float get_float()
|
||||
{
|
||||
return (float)length / (float)clock_rate;
|
||||
}
|
||||
|
||||
inline bool operator < (Time other)
|
||||
{
|
||||
return other.clock_rate * length < clock_rate * other.length;
|
||||
}
|
||||
|
||||
inline Time operator + (Time other)
|
||||
{
|
||||
Time result;
|
||||
result.clock_rate = NumberTheory::least_common_multiple(clock_rate, other.clock_rate);
|
||||
result.length = length * (result.clock_rate / clock_rate) + other.length * (result.clock_rate / other.clock_rate);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline Time &operator += (Time other)
|
||||
{
|
||||
unsigned int combined_clock_rate = NumberTheory::least_common_multiple(clock_rate, other.clock_rate);
|
||||
length = length * (combined_clock_rate / clock_rate) + other.length * (combined_clock_rate / other.clock_rate);
|
||||
clock_rate = combined_clock_rate;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Time operator - (Time other)
|
||||
{
|
||||
Time result;
|
||||
result.clock_rate = NumberTheory::least_common_multiple(clock_rate, other.clock_rate);
|
||||
result.length = length * (result.clock_rate / clock_rate) - other.length * (result.clock_rate / other.clock_rate);
|
||||
return result;
|
||||
}
|
||||
|
||||
inline Time operator -= (Time other)
|
||||
{
|
||||
unsigned int combined_clock_rate = NumberTheory::least_common_multiple(clock_rate, other.clock_rate);
|
||||
length = length * (combined_clock_rate / clock_rate) - other.length * (combined_clock_rate / other.clock_rate);
|
||||
clock_rate = combined_clock_rate;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Time operator * (Time other)
|
||||
{
|
||||
Time result;
|
||||
result.clock_rate = clock_rate * other.clock_rate;
|
||||
result.length = length * other.length;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline Time &operator *= (Time other)
|
||||
{
|
||||
length *= other.length;
|
||||
clock_rate *= other.clock_rate;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Time operator / (Time other)
|
||||
{
|
||||
Time result;
|
||||
result.clock_rate = clock_rate * other.length;
|
||||
result.length = length * other.clock_rate;
|
||||
return result;
|
||||
}
|
||||
|
||||
inline Time &operator /= (Time other)
|
||||
{
|
||||
length *= other.clock_rate;
|
||||
clock_rate *= other.length;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void set_zero()
|
||||
{
|
||||
length = 0;
|
||||
clock_rate = 1;
|
||||
}
|
||||
|
||||
inline void set_one()
|
||||
{
|
||||
length = 1;
|
||||
clock_rate = 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif /* Storage_h */
|
||||
|
@ -29,6 +29,18 @@ void TimedEventLoop::reset_timer()
|
||||
_stepper.reset();
|
||||
}
|
||||
|
||||
void TimedEventLoop::reset_timer_to_offset(Time offset)
|
||||
{
|
||||
unsigned int common_clock_rate = NumberTheory::least_common_multiple(offset.clock_rate, _event_interval.clock_rate);
|
||||
_time_into_interval = offset.length * (common_clock_rate / offset.clock_rate);
|
||||
_event_interval.length *= common_clock_rate / _event_interval.clock_rate;
|
||||
_event_interval.clock_rate = common_clock_rate;
|
||||
if(common_clock_rate != _stepper->get_output_rate())
|
||||
{
|
||||
_stepper.reset(new SignalProcessing::Stepper(_event_interval.clock_rate, _input_clock_rate));
|
||||
}
|
||||
}
|
||||
|
||||
void TimedEventLoop::jump_to_next_event()
|
||||
{
|
||||
reset_timer();
|
||||
@ -70,3 +82,10 @@ void TimedEventLoop::set_next_event_time_interval(Time interval)
|
||||
_stepper.reset(new SignalProcessing::Stepper(_event_interval.clock_rate, _input_clock_rate));
|
||||
}
|
||||
}
|
||||
|
||||
Time TimedEventLoop::get_time_into_next_event()
|
||||
{
|
||||
Time result = _event_interval;
|
||||
result.length = _time_into_interval;
|
||||
return result;
|
||||
}
|
||||
|
@ -66,6 +66,11 @@ namespace Storage {
|
||||
*/
|
||||
void reset_timer();
|
||||
|
||||
/*!
|
||||
Sets the amount of time into the current event to @c offset.
|
||||
*/
|
||||
void reset_timer_to_offset(Time offset);
|
||||
|
||||
/*!
|
||||
Causes an immediate call to @c process_next_event and a call to @c reset_timer with the
|
||||
net effect of processing the current event immediately and fast forwarding exactly to the
|
||||
@ -73,6 +78,12 @@ namespace Storage {
|
||||
*/
|
||||
void jump_to_next_event();
|
||||
|
||||
/*!
|
||||
@returns the amount of time that has passed since the last call to @c set_next_time_interval,
|
||||
which will always be less than or equal to the time that was supplied to @c set_next_time_interval.
|
||||
*/
|
||||
Time get_time_into_next_event();
|
||||
|
||||
private:
|
||||
unsigned int _input_clock_rate;
|
||||
Time _event_interval;
|
||||
|
Loading…
x
Reference in New Issue
Block a user