TestZeroCrossingClock Icon

Examples.TestZeroCrossingClock

Test component that validates the ZeroCrossingClock component.

The idea here is that you have two ways to test the zero crossing clock:

  • the sampled value,
  • and the time between samples.

Currently it's not possible to get the time between samples on an event clock - when it is, we'll update this component to add that as well.

TODO: this component is currently functionally broken because the zero crossing clock is not triggering on all of the zero crossings of the variable-frequency cosine... The reason is that MTK is able to elide the sine from the dynamics of the system, and the only other dynamics are linear from the ramp. So the integrator is able to take very large steps and misses a lot of zero crossings because of that.

The proposed solution is to mark the input of the zero crossing as irreducible, but that introduces an algebraic constraint that does not seem to solve right.

Usage

DiscreteComponents.Examples.TestZeroCrossingClock()

Behavior

\[ \begin{equation} \left[ \begin{array}{c} \mathrm{connect}\left( constant1_{+}y(t), cosinevariablefrequencyandamplitude_{+}amplitude(t) \right) \\ \mathrm{connect}\left( ramp_{+}y(t), cosinevariablefrequencyandamplitude_{+}frequency(t) \right) \\ \mathrm{connect}\left( cosinevariablefrequencyandamplitude_{+}y(t), sampler_{+}u(t) \right) \\ \mathrm{connect}\left( cosinevariablefrequencyandamplitude_{+}y(t), zerocrossingclockblock_{+}u(t) \right) \\ \mathrm{connect}\left( sampler_{+}y(t), zerocrossingclockblock_{+}y(t) \right) \\ \mathrm{connect}\left( sampler_{+}y(t), terminator_{+}u(t) \right) \\ \frac{\mathrm{d} \mathtt{cosinevariablefrequencyandamplitude.phi}\left( t \right)}{\mathrm{d}t} = 6.2832 \mathtt{cosinevariablefrequencyandamplitude.frequency}\left( t \right) \\ \mathtt{cosinevariablefrequencyandamplitude.y}\left( t \right) = \mathtt{cosinevariablefrequencyandamplitude.offset} + \cos\left( \mathtt{cosinevariablefrequencyandamplitude.phi}\left( t \right) \right) \mathtt{cosinevariablefrequencyandamplitude.amplitude}\left( t \right) \\ \mathtt{constant1.y}\left( t \right) = \mathtt{constant1.k} \\ \mathtt{ramp.y}\left( t \right) = ifelse\left( \mathtt{ramp.start\_time} < t, ifelse\left( t < \mathtt{ramp.duration} + \mathtt{ramp.start\_time}, \mathtt{ramp.offset} + \frac{\mathtt{ramp.height} \left( - \mathtt{ramp.start\_time} + t \right)}{\mathtt{ramp.duration}}, \mathtt{ramp.height} + \mathtt{ramp.offset} \right), \mathtt{ramp.offset} \right) \\ \mathtt{sampler.y}\left( t \right) = Sample(ModelingToolkit.InferredClock.var"typeof(InferredClock)"(ModelingToolkit.InferredClock.var"##Storage#InferredDiscrete"(0)))\left( \mathtt{sampler.u}\left( t \right) \right) \\ \end{array} \right] \end{equation} \]

Source

"""
Test component that validates the ZeroCrossingClock component.

The idea here is that you have two ways to test the zero crossing clock: 
- the sampled value, 
- and the time between samples.

Currently it's not possible to get the time between samples on an event clock - when it is,
we'll update this component to add that as well.

TODO: this component is currently functionally broken because the zero crossing
clock is not triggering on all of the zero crossings of the variable-frequency cosine...
The reason is that MTK is able to elide the sine from the dynamics of the system,
and the only other dynamics are linear from the ramp.  So the integrator is able 
to take very large steps and misses a lot of zero crossings because of that.

The proposed solution is to mark the input of the zero crossing as irreducible, but that introduces
an algebraic constraint that does not seem to solve right.
"""
component TestZeroCrossingClock
  cosinevariablefrequencyandamplitude = BlockComponents.CosineVariableFrequencyAndAmplitude() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 80, "y1": 400, "x2": 180, "y2": 500, "rot": 0}
      },
      "tags": []
    }
  }
  constant1 = BlockComponents.Constant(k = 1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -180, "y1": 320, "x2": -80, "y2": 420, "rot": 0}
      },
      "tags": []
    }
  }
  ramp = BlockComponents.Ramp(offset = 1, duration = 20, height = 5) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -180, "y1": 470, "x2": -80, "y2": 570, "rot": 0}
      },
      "tags": []
    }
  }
  sampler = DiscreteComponents.Sampler() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 340, "y1": 400, "x2": 440, "y2": 500, "rot": 0}
      },
      "tags": []
    }
  }
  zerocrossingclockblock = DiscreteComponents.ZeroCrossingClock() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 340, "y1": 260, "x2": 440, "y2": 360, "rot": 0}
      },
      "tags": []
    }
  }
  terminator = BlockComponents.Terminator() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 530, "y1": 400, "x2": 630, "y2": 500, "rot": 0}
      },
      "tags": []
    }
  }
relations
  connect(constant1.y, cosinevariablefrequencyandamplitude.amplitude) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": -35, "y": 370}, {"x": -35, "y": 424}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, cosinevariablefrequencyandamplitude.frequency) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": -35, "y": 520}, {"x": -35, "y": 473}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(cosinevariablefrequencyandamplitude.y, sampler.u) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
  connect(cosinevariablefrequencyandamplitude.y, zerocrossingclockblock.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 230, "y": 450}, {"x": 230, "y": 310}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(zerocrossingclockblock.y, sampler.y) {
    "Dyad": {
      "renderStyle": "standard",
      "edges": [{"S": 1, "M": [{"x": 481, "y": 310}, {"x": 481, "y": 450}], "E": 2}]
    }
  }
  connect(sampler.y, terminator.u) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
metadata {
  "Dyad": {
    "experiments": {},
    "tests": {},
    "labels": [
      {
        "label": "TODO: add a sample time block here, so we can test time between samples",
        "x": 500,
        "y": 750,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      },
      {
        "label": "The zero crossing clock's `y` variable forces the sampler's output to be on the zero crossing clock",
        "x": 530,
        "y": 170,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      },
      {
        "label": "a nontrivial function",
        "x": 100,
        "y": 370,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      }
    ],
    "icons": {"default": "dyad://Dyad/Default.svg"},
    "path": {},
    "doc": {"behavior": true}
  }
}
end
Flattened Source
"""
Test component that validates the ZeroCrossingClock component.

The idea here is that you have two ways to test the zero crossing clock: 
- the sampled value, 
- and the time between samples.

Currently it's not possible to get the time between samples on an event clock - when it is,
we'll update this component to add that as well.

TODO: this component is currently functionally broken because the zero crossing
clock is not triggering on all of the zero crossings of the variable-frequency cosine...
The reason is that MTK is able to elide the sine from the dynamics of the system,
and the only other dynamics are linear from the ramp.  So the integrator is able 
to take very large steps and misses a lot of zero crossings because of that.

The proposed solution is to mark the input of the zero crossing as irreducible, but that introduces
an algebraic constraint that does not seem to solve right.
"""
component TestZeroCrossingClock
  cosinevariablefrequencyandamplitude = BlockComponents.CosineVariableFrequencyAndAmplitude() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 80, "y1": 400, "x2": 180, "y2": 500, "rot": 0}
      },
      "tags": []
    }
  }
  constant1 = BlockComponents.Constant(k = 1) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -180, "y1": 320, "x2": -80, "y2": 420, "rot": 0}
      },
      "tags": []
    }
  }
  ramp = BlockComponents.Ramp(offset = 1, duration = 20, height = 5) {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": -180, "y1": 470, "x2": -80, "y2": 570, "rot": 0}
      },
      "tags": []
    }
  }
  sampler = DiscreteComponents.Sampler() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 340, "y1": 400, "x2": 440, "y2": 500, "rot": 0}
      },
      "tags": []
    }
  }
  zerocrossingclockblock = DiscreteComponents.ZeroCrossingClock() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 340, "y1": 260, "x2": 440, "y2": 360, "rot": 0}
      },
      "tags": []
    }
  }
  terminator = BlockComponents.Terminator() {
    "Dyad": {
      "placement": {
        "diagram": {"iconName": "default", "x1": 530, "y1": 400, "x2": 630, "y2": 500, "rot": 0}
      },
      "tags": []
    }
  }
relations
  connect(constant1.y, cosinevariablefrequencyandamplitude.amplitude) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": -35, "y": 370}, {"x": -35, "y": 424}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(ramp.y, cosinevariablefrequencyandamplitude.frequency) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": -35, "y": 520}, {"x": -35, "y": 473}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(cosinevariablefrequencyandamplitude.y, sampler.u) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
  connect(cosinevariablefrequencyandamplitude.y, zerocrossingclockblock.u) {
    "Dyad": {
      "edges": [{"S": 1, "M": [{"x": 230, "y": 450}, {"x": 230, "y": 310}], "E": 2}],
      "renderStyle": "standard"
    }
  }
  connect(zerocrossingclockblock.y, sampler.y) {
    "Dyad": {
      "renderStyle": "standard",
      "edges": [{"S": 1, "M": [{"x": 481, "y": 310}, {"x": 481, "y": 450}], "E": 2}]
    }
  }
  connect(sampler.y, terminator.u) {"Dyad": {"renderStyle": "standard", "edges": [{"S": 1, "E": 2, "M": []}]}}
metadata {
  "Dyad": {
    "experiments": {},
    "tests": {},
    "labels": [
      {
        "label": "TODO: add a sample time block here, so we can test time between samples",
        "x": 500,
        "y": 750,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      },
      {
        "label": "The zero crossing clock's `y` variable forces the sampler's output to be on the zero crossing clock",
        "x": 530,
        "y": 170,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      },
      {
        "label": "a nontrivial function",
        "x": 100,
        "y": 370,
        "rot": 0,
        "layer": "diagram",
        "attrs": {
          "fill": "black",
          "font-size": "20",
          "dominant-baseline": "central",
          "text-anchor": "middle"
        }
      }
    ],
    "icons": {"default": "dyad://Dyad/Default.svg"},
    "path": {},
    "doc": {"behavior": true}
  }
}
end


Test Cases

No test cases defined.

  • Examples
  • Experiments
  • Analyses